from nevow import loaders
from nevow import tags
from nevow import entities
from nevow import rend
from nevow import guard
from nevow import inevow
from nevow import static
from formless import annotate
from formless import webform

from twisted.web import resource, util
from twisted.internet import defer, error
from twisted.internet.interfaces import IFinishableConsumer

from activist.DataStore import DataStore
from activist.MetaData import MetaData
from activist.Config import Config
from activist.Users import Users
from activist.web import filter
from activist.web import view
import activist.Person
import activist.Comment
import activist.Event
import activist.EventInformation
import activist.Media
import activist.Alias
import activist.Place
from activist.CommandProcessor import CommandProcessor
import activist.text
from activist.web.util import makeURL, makeTimeURLFromEvent, makeRelative
from activist.web.util import addGetVariable
from activist import errors

import time
import os.path
import string
import calendar

STANDARD_LINKS = [
    ('help','/help','Activist Network Help'),
    ('copyright','/copyright','Activist Network Copyright'),
    ('top','/','Activist Network Main Menu'),
    ('made','/contact','Activist Network Contact Information'),
    ('search','/search','Search the Activist Network'),
    ('shortcut icon','/favicon.ico',''),
    ('stylesheet','/activist.css','main')
    ]


class StaticPage(static.File):
    """
    this is to unify the names in the template's MAP
    files. the hack below is because, by default, [some]
    classes get a docFactory arg passed which is a
    nevow.loaders.htmlfile instance
    """

    def __init__(self,filename,overrideType=None):
        print 'serving static file "' + filename + '"'
        static.File.__init__(self,filename)
        print self.fp
        if overrideType:
            self.type = overrideType
            self.encoding = None

class GenericPage(rend.Page):
    """things which all pages might use."""
    docFactory = loaders.htmlfile(Config.template('generic.html'))
    watermark = None #'help', 'participate', 'event'

    def beforeRender(self, ctx):
        ## WARNING FIXME !MW! this is the same as resource.ResourceGeneric.beforeRender
        request = inevow.IRequest(ctx)
        mind = request.session.mind

        mind._optionsFromArgs(request.args)
        
        if request.args.has_key('delete'):
            id = request.args['listingid'][0]
            print "DELETE!",id
            del request.args['delete']
            del request.args['listingid']
            try:
                edit = DataStore[id]
                if request.session.mind.canEdit(edit):
                    edit.delete(request.session.mind.person())
                    print "   deleted!"
                else:
                    print "   can't do that!"
            except KeyError:
                pass
            
        if request.args.has_key('approve'):
            id = request.args['listingid'][0]
            data = DataStore[id]
            if mind.canModerate(data):
                if hasattr(data,'info'):
                    data.info().approve(mind.person())
                else:
                    data.approve(mind.person())
            del request.args['approve']
            del request.args['listingid']
        elif request.args.has_key('unapprove'):
            id = request.args['listingid'][0]
            data = DataStore[id]
            if mind.canModerate(data):
                if hasattr(data,'info'):
                    data.info().unapprove(mind.person())
                else:
                    data.unapprove(mind.person())
            del request.args['unapprove']
            del request.args['listingid']

    def URI(self,request):
        rtn = request.uri
        if '?' in rtn:
            return rtn.split('?')[0]
        return rtn

    def addArg(self,request,key,value):
        uri = request.uri
        if '?' in uri:
            old = uri.split('?')[1]
            uri = uri.split('?')[0] + "?"
            for x in old.split('&'):
                (k,v) = x.split('=')
                if k == '__session_just_started__': continue
                if k != key:
                    uri = uri + str(k) + "=" + str(v) + "&"

            return uri + str(key) + "=" + str(value)
        return uri + "?" + str(key) + "=" + str(value)

    def data_id(self,ctx,data):
        if inevow.IRequest(ctx).session.mind.object is None:
            return 'NO OBJECT'
        return inevow.IRequest(ctx).session.mind.object.id()

    def render_body(self,ctx,data):
        rtn = ctx.tag
        for x in dir(inevow.IRequest(ctx).session.mind.object):
            try:
                rtn = rtn[tags.div[x,': ',str(getattr(inevow.IRequest(ctx).session.mind.object,x))]]
            except AttributeError:
                pass
        return rtn[tags.div[str(inevow.IRequest(ctx).session.mind.object)]]

    def render_summaryview(self,ctx,data):
        return ctx.tag[tags.div(render=lambda ctx,data:view.ISummaryView(data),data=data)]
    def render_fullview(self,ctx,data):
        return ctx.tag[tags.div(render=lambda ctx,data:view.IFullView(data),data=data)]
    def render_microview(self,ctx,data):
        return ctx.tag[tags.div(render=lambda ctx,data:view.IMicroView(data),data=data)]

    def data_type(self,ctx,data):
        if inevow.IRequest(ctx).session.mind.object is None:
            return 'NO OBJECT'
        return str(inevow.IRequest(ctx).session.mind.object.__class__)[8:-2].split('.')[2]

    def crumbExtras(self,ctx):
        return []

    def render_banner(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = []

        login = tags.div(id="login")
        ctx.tag = login
        rtn.append(self.render_login(ctx,data))

        banner = tags.div(id="banner")
        ctx.tag = banner
        rtn.append(self.render_menu(ctx,data))

        if Config.is_beta:
            beta = tags.div(id='beta')[tags.b['Note:'], 'This software is still ', tags.a(href=makeRelative(request.uri,'/help/beta-testing'))['beta quality'], '.']
            return [rtn,beta]
        return rtn

    def render_menu(self,ctx,data):
        request = inevow.IRequest(ctx)

        menu = []
        submenu = []

        menu.append(tags.li[tags.a(id='main',href=makeRelative(request.uri,'/'))['Site Map']])
        menu.append(tags.li[tags.a(href=makeRelative(request.uri,'/event/time'))['Event Listings']])
        menu.append(tags.li[tags.a(href=makeRelative(request.uri,'/help'))['Help and History']])
        
        if request.session.mind.isAnonymous():
            menu.append( tags.li[tags.a(href=makeRelative(request.uri,'/event/create'))['Add Listing']] )
                           
        else:
            menu.append(tags.li[tags.a(href=makeRelative(request.uri,'/event/create'))['Add Listing']])
            person = request.session.mind.person()
            if person.isAdministrator():
                submenu.append(tags.li[tags.a(href=makeRelative(request.uri,'/admin'))['Administrate']])
                submenu.append(tags.li[tags.a(href=makeRelative(request.uri,'/create/group'))['Add Group']])
            if person.isAdministrator():
                submenu.append(tags.li[tags.a(href=makeRelative(request.uri,'/create/article'))['Add Article']])
            if request.session.mind.canCreateMedia():
                submenu.append(tags.li[tags.a(href=makeRelative(request.uri,'/create/media'))['Add Media']])
            

        if len(submenu) > 0:
            return ctx.tag[tags.ul[menu], tags.div(id="submenu")[tags.ul[submenu]]]
        return ctx.tag[tags.ul[menu]]

    def render_login(self,ctx,data):
        request = ctx.locate(inevow.IRequest)
        mind = request.session.mind
        redir = self.URI(request)#str(request.path)
        
        if mind.isAnonymous():
            return ctx.tag[tags.a(href=makeRelative(request.uri,'/login')+'?redirect=%s'%request.uri)['login'],' or ',
                           tags.br,tags.a(href=makeRelative(request.uri,'/login/create'))['register'],
                           tags.span(style='font-size:80%;')[' (',tags.a(href=makeRelative(request.uri,'/help/registering'),title='Why would I want to register?')['?'],')']]

        return ctx.tag[
            tags.a(href=makeRelative(request.uri,'/person/'+mind.person().id()))[mind.username()],
            ' (',tags.a(href=makeRelative(request.uri,'/logout')+'?redirect=%s'%redir)['logout'],') ']

    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = []
        for (rel,href,title) in STANDARD_LINKS:
            rtn.append(tags.link(rel=rel,href=makeRelative(request.uri,href),title=title))
        return rtn

    def render_watermark(self,ctx,data):
        if self.watermark:
            return ctx.tag(id=self.watermark)
        return ''

    def render_crumb(self,ctx,data):
        """
        <a href="%s">splash</a> &lt; <a href="%s">help</a> &lt; about
        """
        
        request = inevow.IRequest(ctx)
        ctx.tag[ #tags.a(href='/')[tags.img(height='16',width='16',src='/icon.png',style='border:none;')],
                     'you are here: ', tags.a(href=makeRelative('/',request.uri))['activist.ca'],
                     ' < ',
                     self.crumbExtras(ctx)
                     ]
        return ctx.tag
    
    def render_connections(self,ctx,data):
        return ''
        request = inevow.IRequest(ctx)
        if request.session.mind.object is None:
            return 'NO OBJECT'
        meta = []
        for m in request.session.mind.object.metadata():
            if str(m.relationship())[:11] == 'DC.Relation':
                relationship = string.replace(string.ljust(str(m.relationship())[11:],25),' ','_').upper()
            else:
                relationship = string.replace(string.ljust(str(m.relationship()),25),' ','_').upper()
            creator = '<None'
            if m.creator():
                creator = tags.a(href="/object/%s"%m.creator().id())[m.creator().info()._name]
            body = tags.span[tags.a(href=makeURL(request.uri, m.fromData()))[m.fromData().id()],tags.span['_'+relationship],tags.a(href=makeURL(request.uri, m.toData()))[m.toData().id()],'( ',creator,' )']
            meta.append(tags.li[tags.a(href="/object/%s"%m.id())[m.id()],':',body])
        return ctx.tag(style='border: 1px solid black; float:right;clear:right; font-family: fixed; font-size: 60%; margin-top: 2em;')[
            tags.h1(style='border:0;padding:0;margin:0;')['debug metadata'],
            tags.ul[meta]
            ]


class Error404(GenericPage):
    docFactory = loaders.htmlfile(Config.template('404.html'))

    def renderHTTP(self, ctx):
        request = inevow.IRequest(ctx)
        request.setResponseCode(404)
        return GenericPage.renderHTTP(self,ctx)
        
    def render_path(self,ctx,data):
        return ctx.tag['/' + '/'.join(inevow.IRequest(ctx).prepath)]

class YearTooBig(GenericPage):
    docFactory = loaders.htmlfile(Config.template('year-too-big.html'))

    def renderHTTP(self, ctx):
        request = inevow.IRequest(ctx)
        request.setResponseCode(404)
        return GenericPage.renderHTTP(self,ctx)
        
    def render_today(self,ctx,data):
        request = inevow.IRequest(ctx)
        return ctx.tag(href=makeRelative(request.uri,'/event/time'))


# !mw! FIXME XXX should conbine with resource.Admin to avoid confusion...
class AdminPage(GenericPage):
    docFactory = loaders.htmlfile(Config.template('admin.html'))
    watermark = 'participate'

    def _makeData(self):
        class data(object):
            pass
        return data()
    _makeData = classmethod(_makeData)

    def beforeRender(self,ctx):
        GenericPage.beforeRender(self,ctx)
        
        self.checkpointed = False
        
        request = inevow.IRequest(ctx)
        if request.session.mind.canAdmin():
            if request.args.has_key('checkpoint'):
                from activist.CommandProcessor import performCheckpoint
                performCheckpoint(False)
                del request.args['checkpoint']
                self.checkpointed = True

            if request.args.has_key('siteconfig'):
                try:
                    spc = int(request.args['seconds_per_checkpoint'][0])
                    if spc >= 3600:
                        Config.seconds_per_checkpoint = spc
                except:
                    pass

                try:
                    evp = int(request.args['events_per_page'][0])
                    Config.events_per_page = evp
                except:
                    pass

                from activist.Category import Category
                all = DataStore.getCategories()
                if request.args.has_key('categories'):
                    print request.args['categories']
                    for x in request.args['categories'][0].split('\r\n'):
                        x = x.strip()
                        if len(x) == 0:
                            continue
                        found = False
                        for y in all:
                            if x == y.title():
                                found = True
                                break
                        if not found:
                            foo = Category()
                            foo.create(x)

                mil = False
                if request.args.has_key('munge_ip_logging'):
                    mil = True
                Config.munge_ip_logging = mil

    def render_title(self,ctx,data):
        return ctx.tag['Administration']

    def render_body(self,ctx,data):
        request = inevow.IRequest(ctx)
        
        if not request.session.mind.canAdmin():
            return ctx.tag[tags.div(_class='error')['You are not authorised to administrate this site.']]

        otherthings = []
        otherthings.append(tags.div[
            tags.p['You may also ',tags.a(href=makeRelative(request.uri,"/admin/messages"))['read messages to the moderators.']]
            ])

        if request.args.has_key('reallyreboot'):
            reboot = int(request.args['reallyreboot'][0])
            try:
                CommandProcessor.restartToSpecificConsistencyPoint(reboot)
            except:
                return ctx.tag[
                    tags.div(_class='error')['Failed to issue the restart command. Contact a developer or other administrator for more help.']
                    ]
            
            return ctx.tag[
                tags.div(_class='error')['In 10 seconds, I will reboot the application server to roll-back point #%d. The application server will come back up in a few minutes. If it remains unresponsive after 10 minutes, please contact one of the other administrators or developers.'%reboot]
                ]
                

        if request.args.has_key('reboot'):
            try:
                reboot = int(request.args['reboot'][0])
            except:
                return 'Illegal argument for reboot.'

            remaining = CommandProcessor.count - reboot
            
            return ctx.tag[
                tags.div(_class='error')[
                tags.p[tags.b['WARNING:'],' You are about to reboot the application server to a roll-back point. This will destroy information (%d commands-worth to be precise). If you do not know what you are doing, '%remaining,tags.b['please stop now'],' by pressing the "logout" link in the upper-right.'],
                
                tags.form(action='/admin',method='post')[
                  tags.input(_type='hidden',name='reallyreboot',value=str(reboot)),
                  tags.input(_type='submit',name='submit',value='Yes, really reboot and destroy %d commands-worth of information.'%remaining)
                  ]
                ]
              ]

        
        tasks = []


        categories = []
        for x in DataStore.getCategories():
            categories.append(x.title() + '\n')
            
        config = []
        if Config.munge_ip_logging:
            config.append(tags.div['Munge IP addresses when logging?', tags.input(type='checkbox',name='munge_ip_logging',checked='checked')])
        else:
            config.append(tags.div['Munge IP addresses when logging?', tags.input(type='checkbox',name='munge_ip_logging')])

        config.append(tags.div['Seconds between automatic checkpoints:',
                               tags.input(type='text',name='seconds_per_checkpoint',value=str(Config.seconds_per_checkpoint))])

        config.append(tags.div['Events per page:',
                               tags.input(type='text',name='events_per_page',value=str(Config.events_per_page))])

        config.append(tags.div['Categories:',
                               tags.textarea(rows='10',cols='80',name='categories')[categories]])

        tasks.append(tags.div[
            tags.h2['Site Configuration'],
            tags.form(action=request.uri, method='post')[
            config,
            tags.input(type='submit',name='siteconfig',value='Change site config',_class='button')
            ]])

        consistency = []
        for x in CommandProcessor.consistent:
            consistency.append(tags.a(href='/admin?reboot=%d'%x)['Command #'+str(x)])
            consistency.append(tags.br)
            
        tasks.append(tags.div[
            tags.h2['Consistency Points'],
            tags.p['The datastore has asserted that it is consistent at the following command-points:'],
            tags.p['You may click on a tag to reboot the application-server to that point.',tags.b['WARNING:'],'this will destroy all data after that point. If unsure, do not do this.'],
            tags.p[consistency]
            ])


        if self.checkpointed:
            tasks.append(tags.div[
                tags.h2['Checkpoint'],
                tags.p['Successfully checkpointed.']
                ])
        else:
            interval = Config.seconds_per_checkpoint
            if interval / 60 < 30:
                interval = '%d minutes' % (interval/60,)
            elif interval / 3600 < 30:
                interval = '%d hours' % (interval/3600,)
            else:
                interval = '%d days' % (interval/(3600*24),)
            tasks.append(tags.div[
                tags.h2['Checkpoint'],
                tags.p['Pressing the "immediate checkpoint" button will dump all objects to an XML representation on disc. This may be later used for recovery. ',
                       tags.b['Note, however,'], ' that a checkpoint automatically occurs every %s.' % (interval,)],
                tags.form(action=request.uri, method='post')[
                  tags.input(type='submit',name='checkpoint',value='immediate checkpoint',_class='button')
                  ]]
                     )

        users = tags.div[tags.h2['Users']]
        table = tags.table[tags.tr[tags.th['real name'], tags.th['username'], tags.th['administrator'], tags.th['moderator'], tags.th['comments']]]
        odd = True
        people = []
                                        # FIXME; gross
        for x in DataStore.getByClass(activist.Person.Person):
            people.append(x)
        def byName(a,b):
            return cmp(a.name().lower(),b.name().lower())
        people.sort(byName)
        for x in people:
            if x.username() is None or x.username() == '':
                continue
            tr = tags.tr
            if odd: tr = tags.tr(_class="odd")
            odd = not odd
            admin = tags.input(type="checkbox",name="admin_%s"%x.id())
            moderator = tags.input(type="checkbox",name="moderator_%s"%x.id())
            commentator = tags.input(type="checkbox",name="commentator_%s"%x.id())
            if x.isAdministrator():
                admin(checked="checked")
                if x == request.session.mind.person():
                    admin = tags.input(type="hidden",name="admin_%s"%x.id(),value="on")
            if x.isModerator():
                moderator(checked="checked")
            if x.isCommentator():
                commentator(checked="checked")
            table[tr[
                tags.td[tags.a(href=makeURL(request.uri, x))[x.name()]],
                tags.td[x.username()],
                tags.td(style="text-align:center;")[admin],
                tags.td(style="text-align:center;")[moderator],
                tags.td(style="text-align:center;")[commentator]
                ],
                  tags.input(type="hidden",name="admin_id",value=x.id())]
        users[tags.form(method="post",action=request.uri)[table,
                                                          tags.input(_class="button",type="submit",value="change")]
              ]
        
        return ctx.tag[otherthings,tasks,users]

class AdminMessages(GenericPage):
    docFactory = loaders.htmlfile(Config.template('admin.html'))
    watermark = 'participate'
    requireLogin = True
    
    #placeholder
    def _makeData(self):
        class data(object):
            pass
        return data()
    _makeData = classmethod(_makeData)

    def beforeRender(self,ctx):
        GenericPage.beforeRender(self,ctx)
        request = inevow.IRequest(ctx)
        if request.session.mind.canAdmin():
            if request.args.has_key('delete_message'):
                pass
            #fixme implement FIXME XXX !mw!

    def render_title(self,ctx,data):
        return ctx.tag['Administration Messages']

    def render_body(self,ctx,data):
        rtn = []

        for comment in DataStore.getByClass(activist.Comment.Comment):
            for meta in comment.metadata():
                if meta._relationship == MetaData.CommentOn:
                    continue

            rtn.append(tags.div(render=lambda ctx,d:view.IFullView(d),data=comment))

        return ctx.tag[rtn]
    

class CreateArticlePage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'article-create.html'))
    watermark = 'participate'
   
    def _makeData(self):
        class data(object):
            editing = None
            creating = 0
            preview = 1
            alias = None
            up = None
            next = None
            prev = None
            title = 'Article Title'
            texttype = 'html'
            text = '''<p>The article body.</p>'''
        return data()
    _makeData = classmethod(_makeData)


    def beforeRender(self,ctx):
        GenericPage.beforeRender(self,ctx)
        
        request = inevow.IRequest(ctx)

        if request.args.has_key('edit'):
            id = request.args['edit'][0]
            del request.args['edit']

            if not request.session.mind[CreateArticlePage].editing:
                 try:
                     edit = DataStore[id]
                     request.session.mind[CreateArticlePage].editing = edit
                     request.session.mind[CreateArticlePage].title = edit.title()
                     request.session.mind[CreateArticlePage].up = edit._up
                     request.session.mind[CreateArticlePage].next = edit._next
                     request.session.mind[CreateArticlePage].prev = edit._prev
                     request.session.mind[CreateArticlePage].preview = 1
                     text = edit.content()
                     if text[:6].lower() == '!html!':
                         request.session.mind[CreateArticlePage].text = text[6:]
                         request.session.mind[CreateArticlePage].texttype = 'HTML'
                     elif text[:5].lower() == '!rst!':
                         request.session.mind[CreateArticlePage].text = text[5:]
                         request.session.mind[CreateArticlePage].texttype = 'rST'
                     else:
                         request.session.mind[CreateArticlePage].text = text
                         request.session.mind[CreateArticlePage].texttype = 'html'

                 except KeyError:
                     pass

        if request.args.has_key('title'):
            request.session.mind[CreateArticlePage].title = request.args['title'][0]
        if request.args.has_key('alias'):
            request.session.mind[CreateArticlePage].alias = request.args['alias'][0]
        if request.args.has_key('up'):
            request.session.mind[CreateArticlePage].up = request.args['up'][0]
        if request.args.has_key('prev'):
            request.session.mind[CreateArticlePage].prev = request.args['prev'][0]
        if request.args.has_key('next'):
            request.session.mind[CreateArticlePage].next = request.args['next'][0]
        if request.args.has_key('texttype'):
            request.session.mind[CreateArticlePage].texttype = request.args['texttype'][0]
        if request.args.has_key('text'):
            request.session.mind[CreateArticlePage].text = request.args['text'][0]

        if request.args.has_key('preview'):
            request.session.mind[CreateArticlePage].preview = 1

        if request.args.has_key('create'):
            if request.session.mind[CreateArticlePage].creating == 0:
                request.session.mind[CreateArticlePage].creating = 1
            elif request.session.mind[CreateArticlePage].creating == 1:
                request.session.mind[CreateArticlePage].creating = 2
            else:
                print "CREATING is already:",request.session.mind[CreateArticlePage].creating 

                
        if request.args.has_key('no'):
            request.session.mind[CreateArticlePage].creating = 0
            
            

    def preview(self,request):
        if request.session.mind[CreateArticlePage].preview == 0:
            return ''

        return tags.div(id="preview",style="z-index:-5;")[
          tags.div(_class='listing')[
            tags.div(_class='listingbody')[
              tags.h1[request.session.mind[CreateArticlePage].title],
              tags.xml(activist.text.ToHTML('!' + request.session.mind[CreateArticlePage].texttype + '!\n' + request.session.mind[CreateArticlePage].text))
            ]
          ]
        ]
        
    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = GenericPage.render_links(self,ctx,data)
        rtn.append(tags.link(rel='stylesheet',href=makeRelative(request.uri,'/activist-form.css')))
        return rtn

    def render_form(self,ctx,data):
        request = inevow.IRequest(ctx)

        print "CREATING is:",request.session.mind[CreateArticlePage].creating 

        if request.session.mind[CreateArticlePage].creating > 1:
            extra = []
            from activist.Article import Article
            from activist.Alias import Alias
            from activist.Text import Text

            if request.session.mind[CreateArticlePage].editing:
                edit = request.session.mind[CreateArticlePage].editing
                edit.editmeta(request.session.mind[CreateArticlePage].up, request.session.mind[CreateArticlePage].next, request.session.mind[CreateArticlePage].prev)
                newtext = Text()
                newtext.create('!'+request.session.mind[CreateArticlePage].texttype+'!\n' + request.session.mind[CreateArticlePage].text)
                edit.edit(newtext)
                extra.append(tags.p['Edited article ',tags.a(href=makeURL(request.uri, edit))[edit.title()]])
                article = edit
            else:
                newtext = Text()
                newtext.create('!'+request.session.mind[CreateArticlePage].texttype+'!\n' + request.session.mind[CreateArticlePage].text)
                article = Article(request.session.mind[CreateArticlePage].title,
                                  newtext,
                                  creator = request.session.mind.person(),
                                  up = request.session.mind[CreateArticlePage].up,
                                  next = request.session.mind[CreateArticlePage].next,
                                  prev = request.session.mind[CreateArticlePage].prev)
                if request.session.mind[CreateArticlePage].alias:
                    alias = Alias(request.session.mind[CreateArticlePage].alias,makeURL(request.uri, article),request.session.mind.person(),)
                    print "  ALIAS:",alias
                    extra.append(tags.p['(With alias ',
                                        tags.a(href=request.session.mind[CreateArticlePage].alias)[tags.tt[request.session.mind[CreateArticlePage].alias]],
                                        ' --> ',
                                        tags.tt[makeURL(request.uri, article)],
                                        ').'])
                
            rtn = ctx.tag[
                tags.div(_class='help')[
                  tags.p['You have successfully created a new article with ID: ',
                         tags.a(href=makeURL(request.uri, article))[article.id()],
                         '.  If you like, you may ',
                         tags.a(href=makeRelative(request.uri,'/article/create'))['create another'],
                         '.', extra]
                  ]]
            del request.session.mind[CreateArticlePage]
            return rtn
            


        if not request.session.mind.canAdmin():
            return ctx.tag[tags.div(_class='error')['You are not allowed to create articles']]
        
        createoredit = 'create'
        if request.session.mind[CreateEventPage].editing: createoredit = 'edit'
        if request.session.mind[CreateArticlePage].creating == 1:
            return ctx.tag[
                  ## "really create?"
                  tags.div[
                    tags.form(action="/create/article",method="post")[
                    tags.p['Really ',createoredit,' Article?'],
                    tags.input(type='submit',name='create',value=createoredit,_class='button'),
                    tags.input(type='submit',name='no',value='keep updating',_class='button')
                    ]
                  ],

                  ## preview
                  self.preview(request)
                ]

        types = []
        if request.session.mind[CreateArticlePage].texttype == 'HTML':
            types.append(tags.option(name='texttype',value='HTML',selected='selected')['HTML'])
        else:
            types.append(tags.option(name='texttype',value='HTML')['HTML'])
        if request.session.mind[CreateArticlePage].texttype == 'rST':
            types.append(tags.option(name='texttype',value='rST',selected='selected')['reStructured Text'])
        else:
            types.append(tags.option(name='texttype',value='rST')['reStructured Text'])

        return ctx.tag[
            ## create-article form
            tags.div[
              tags.form(name="createarticle",action=request.uri,method="post")[
              tags.fieldset[
                tags.legend['Article Title'],
                tags.input(type='text',_class='text',size='80',id='title',name='title',value=request.session.mind[CreateArticlePage].title)],

              tags.fieldset[
                tags.legend['Meta Info'],
                tags.label['Alias:',tags.input(type='text',_class='text',size='60',id='alias',name='alias',value=request.session.mind[CreateArticlePage].alias)],tags.br,
                tags.label['Up:',tags.input(type='text',_class='text',size='60',id='up',name='up',value=request.session.mind[CreateArticlePage].up)],tags.br,
                tags.label['Next:',tags.input(type='text',_class='text',size='60',id='next',name='next',value=request.session.mind[CreateArticlePage].next)],tags.br,
                tags.label['Prev:',tags.input(type='text',_class='text',size='60',id='prev',name='prev',value=request.session.mind[CreateArticlePage].prev)]
              ],
                

              tags.fieldset[
                tags.legend['Article Content'],
                
                tags.label['Type',
                           tags.select(name='texttype',id='texttype')[
                             types
                           ]],
                
                tags.fieldset[
                  tags.legend['Text Body'],
                  tags.textarea(style='width:100%;',rows=20,cols=80,name='text',id='text')[request.session.mind[CreateArticlePage].text]]
                ],
              
              tags.input(type='submit',name='preview',value='preview >>',_class='button'),
              tags.input(type='submit',name='create',value='create',_class='button')
              ]
            ],

            self.preview(request)
          ]

class ArticlePage(GenericPage):
    docFactory = loaders.htmlfile(Config.template('article.html'))
    watermark = 'help'

    def crumbExtras(self,ctx):
        rtn = []
        if inevow.IRequest(ctx).session.mind.object._up:
            rtn.append(tags.a(href=inevow.IRequest(ctx).session.mind.object._up)[inevow.IRequest(ctx).session.mind.object._up[1:]])
            rtn.append( ' < ' )
        rtn.append(inevow.IRequest(ctx).session.mind.object.title())
        return rtn
    
    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = GenericPage.render_links(self,ctx,data)
        if request.session.mind.object._up:
            rtn.append(tags.link(rel="up",href=makeRelative(request.uri,request.session.mind.object._up),title=request.session.mind.object._up))
        if request.session.mind.object._next:
            rtn.append(tags.link(rel="next",href=makeRelative(request.uri,request.session.mind.object._next),title=request.session.mind.object._next))
        if request.session.mind.object._prev:
            rtn.append(tags.link(rel="prev",href=makeRelative(request.uri,request.session.mind.object._prev),title=request.session.mind.object._prev))
        return rtn
            
    def render_body(self,ctx,data):
        request = inevow.IRequest(ctx)

        if request.session.mind.canAdmin() and request.uri != makeURL(request.uri, request.session.mind.object):
            ctx.tag[tags.div(_class='infobox')['This is really ',tags.a(href=makeURL(request.uri, request.session.mind.object))[makeURL(None, request.session.mind.object)]]]

        if request.session.mind.canAdmin():
            ctx.tag[tags.div(style='padding:5px; float: right; clear: right;',_class='participate')[tags.a(href=makeRelative(request.uri,'/create/article')+'?edit=%s'%request.session.mind.object.id())['edit']]]
            
        if request.args.has_key('version') and request.session.mind.canAdmin():
            oldversion = request.args['version'][0]

            renderedContent = None
            older = []
            versions = request.session.mind.object.allVersions()
            for x in versions:
                if x.id() == oldversion:
                    older.append(tags.span(style="font-weight:bold;")[x.formatDate('%b %d, %Y @ %H:%M:%S')])
                    renderedContent = activist.text.ToHTML(x.toData().content())

                else:
                    if x == versions[0]:
                        older.append(tags.a(href=self.URI(request))[x.formatDate('%b %d, %Y @ %H:%M:%S')])
                    else:
                        older.append(tags.a(href=self.addGetVariable(request.uri,'version',x.id()))[x.formatDate('%b %d, %Y @ %H:%M:%S')])
                older.append(tags.br)

            if not renderedContent:
                return ctx.tag[tags.div(_class='warning')["Non-existant version."]]

            ctx.tag[tags.div(_class='versions')[tags.div(_class='warning')['Versions:',tags.br,older]]]
            return ctx.tag[tags.xml(renderedContent)]
        
        old = request.session.mind.object.allVersions()
        if len(old) > 1:
            older = []
            for x in old:
                if x == old[0]:
                    older.append(tags.span(style="font-weight:bold;")[x.formatDate('%b %d, %Y @ %H:%M:%S')])
                else:
                    older.append(tags.a(href=self.URI(request)+'?version=%s'%x.id())[x.formatDate('%b %d, %Y @ %H:%M:%S')])
                older.append(tags.br)
            if not request.session.mind.isAnonymous():
                ctx.tag[tags.div(_class='versions')['Versions:',tags.br,older]]
        return ctx.tag[tags.xml(activist.text.ToHTML(request.session.mind.object.content()))]

    def render_title(self,ctx,data):
        return ctx.tag[inevow.IRequest(ctx).session.mind.object.title()]

class WaitPage(GenericPage):
    """things which all pages might use."""
    docFactory = loaders.htmlfile(Config.template('wait.html'))

    def render_redirect(self,ctx,data):
        request = ctx.locate(inevow.IRequest)
        if request.args.has_key('redirect'):
            redir = request.args['redirect'][0]
            ctx.tag(content='0;url=%s'%redir)
        return ctx.tag

    def render_message(self,ctx,data):
        request = ctx.locate(inevow.IRequest)
        if request.args.has_key('message'):
            ctx.tag.clear()
            ctx.tag[request.args['message'][0]]
            
        return ctx.tag

class PersonPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'person-fullview-page.html'))
    watermark = 'event'

    def crumbExtras(self,ctx):
        return [tags.a(href=makeRelative(request.uri,'/person/'))['person'],
                ' < ',
                inevow.IRequest(ctx).session.mind.object.info()._name ]

    def render_body(self,ctx,data):
        data = inevow.IRequest(ctx).session.mind.object
        print "private?",data.private()
        if data.private():
            if inevow.IRequest(ctx).session.mind.canAdmin():
                return [tags.div(_class='help')['Note: this user is private, but you are an admin.'],
                        tags.div(render=lambda ctx,data:view.IFullView(data),data=data)]
            return tags.div(_class='error')['This user has requested privacy. Only administrators may view this page.']
        
        return tags.div(render=lambda ctx,data:view.IFullView(data),data=data)

    def render_title(self,ctx,data):
        return ctx.tag[inevow.IRequest(ctx).session.mind.object.info()._name]
    
class PlacePage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'place-fullview-page.html'))
    watermark = 'event'

    def locateChild(self, ctx, segments):
        request = ctx.locate(inevow.IRequest)

        try:
            object = DataStore[segments[-1]]
        except KeyError:
            object = None

        inevow.IRequest(ctx).session.mind.object = object
        return self, ()

    def crumbExtras(self,ctx):
        return [tags.a(href=makeRelative(request.uri,'/place/'))['place'],
                ' < ',
                inevow.IRequest(ctx).session.mind.object._name ]

    def render_body(self,ctx,data):
        return tags.div(render=lambda ctx,data:view.IFullView(data),data=inevow.IRequest(ctx).session.mind.object)

    def render_title(self,ctx,data):
        return ctx.tag[inevow.IRequest(ctx).session.mind.object._name]
    
class CommentPage(GenericPage):
    docFactory = loaders.htmlfile(Config.template('comment-fullview-page.html'))
    watermark = 'participate'

    def crumbExtras(self,ctx):
        return [tags.a(href=makeRelative(request.uri,'/comment/'))['comment'],
                ' < ',
                inevow.IRequest(ctx).session.mind.object.title() ]

    def render_body(self,ctx,data):
        data = inevow.IRequest(ctx).session.mind.object
        return tags.div(render=lambda ctx,data:view.IFullView(data),data=data)

    def render_title(self,ctx,data):
        return ctx.tag[inevow.IRequest(ctx).session.mind.object.title()]

    def render_context(self,ctx,data):
        contexts = []
        request = inevow.IRequest(ctx)
        self.recursiveContext(request.session.mind.object,contexts,request)
        ctx.tag['View in context with ']
        for x in contexts:
            ctx.tag[x,", "]
        return ctx.tag

    def recursiveContext(self,obj,rtn,request):
        for x in obj.targetsForRelationship(MetaData.CommentOn):
            if isinstance(x,activist.Event.Event):
                rtn.append(tags.a(href=makeRelative(request.uri,'/event')+'/%s'%x.id())['event:',x.info()._title])
            elif isinstance(x,activist.Comment.Comment):
                rtn.append(tags.a(href=makeRelative(request.uri,'/comment')+'/%s'%x.id())['comment:',x.title()])
            self.recursiveContext(x,rtn,request)
        return

class CreateCommentPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'comment-create.html'))
    watermark = 'participate'

    def _makeData(self):
        class data(object):
            creating = 0
            title = ''
            body = ''
            commentOn = None
        return data()
    _makeData = classmethod(_makeData)

    
    def beforeRender(self,ctx):
        GenericPage.beforeRender(self,ctx)
        
        request = inevow.IRequest(ctx)
        
        if request.args.has_key('comment_on'):
            try:
                commenton = DataStore[request.args['comment_on'][0]]
                if isinstance(commenton,activist.Event.Event) or \
                       isinstance(commenton,activist.Comment.Comment):
                    request.session.mind[CreateCommentPage].commentOn = commenton
                else:
                    request.session.mind[CreateCommentPage].commentOn = None
                    print "not allowed to comment on:",commenton
            except KeyError:
                request.session.mind[CreateCommentPage].commentOn = None

        if request.args.has_key('create'):
            if request.session.mind[CreateCommentPage].creating < 1:
                request.session.mind[CreateCommentPage].creating = 1
                
            else:
                person = None
                if not request.session.mind.isAnonymous():
                    person = request.session.mind.person()
                comment = activist.Comment.Comment()
                comment.create( request.session.mind[CreateCommentPage].commentOn,
                                request.session.mind[CreateCommentPage].title,
                                request.session.mind[CreateCommentPage].body,
                                person )
                if request.session.mind.canModerate(comment):
                    comment.approve(request.session.mind.person())
                request.session.mind[CreateCommentPage].creating = 2
                request.session.mind[CreateCommentPage].commentId = comment.id()
                request.session.mind[CreateCommentPage].redirect = '/comment/%s'%comment.id()

                if not request.session.mind[CreateCommentPage].commentOn:
                    from activist.util import sendMessageToAllAdministrators
                    name = 'Anonymous'
                    if person:
                        name = person.info()._name + ' (id: ' + person.id() + ')'
                    sendMessageToAllAdministrators('Comment to Moderators from %s:\n\n%s\n\n%s' % (name,request.session.mind[CreateCommentPage].title,request.session.mind[CreateCommentPage].body))
                
                if isinstance(request.session.mind[CreateCommentPage].commentOn,activist.Event.Event):
                    request.session.mind[CreateCommentPage].redirect = '/event/%s'%request.session.mind[CreateCommentPage].commentOn.id()

                else:
                    if request.session.mind[CreateCommentPage].commentOn:
                        request.session.mind[CreateCommentPage].redirect = '/object/%s'%request.session.mind[CreateCommentPage].commentOn.id()
                    else:
                        request.session.mind[CreateCommentPage].redirect = '/help/moderators'
        else:
            request.session.mind[CreateCommentPage].creating = 0
            
        if request.args.has_key('title'):
            request.session.mind[CreateCommentPage].title = request.args['title'][0]
        if request.args.has_key('body'):
            request.session.mind[CreateCommentPage].body = request.args['body'][0]


    def preview(self,request):
        if request.session.mind.person():
            name = request.session.mind.person().info()._name
        else:
            name = tags.span(_class="anonymous")['ANONYMOUS']
        return tags.div(_class='comment')[
            tags.h4[request.session.mind[CreateCommentPage].title,' (by: ',name,')'],
            tags.xml(activist.text.ToHTML(request.session.mind[CreateCommentPage].body))
            ]
    

    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = GenericPage.render_links(self,ctx,data)
        rtn.append(tags.link(rel='stylesheet',href=makeRelative(request.uri,'/activist-form.css')))
        return rtn

    def crumbExtras(self,ctx):
        return [ 'comment' ]

    def render_target(self,ctx,data):
        request = ctx.locate(inevow.IRequest)
        if not request.args.has_key('comment_on'):
            return ctx.tag['You are sending a general comment to the ',
                               tags.a(href=makeRelative(request.uri,'/help/moderators'))['moderators'],
                               '.']
        
        id = request.args['comment_on'][0]
        obj = DataStore[id]
        ctx.tag.clear()

        if isinstance(obj,activist.Event.Event):
            return ctx.tag['You are creating a comment about "',
                               tags.a(href=makeRelative(request.uri,'/event')+'/%s'%obj.id())[obj.info().title()],
                               '".']
        elif isinstance(obj,activist.Comment.Comment):
            return ctx.tag['You are replying to comment "',
                               tags.a(href=makeRelative(request.uri,'/comment')+'/%s'%obj.id())[obj.title()],
                               '".']
        return ctx.tag['You are creating a comment about some non-Event object.']

    def render_form(self,ctx,data):
        request = ctx.locate(inevow.IRequest)

        if request.session.mind[CreateCommentPage].commentOn is not None:
            if not request.session.mind.canComment(request.session.mind[CreateCommentPage].commentOn):
                return ctx.tag[tags.div(_class='error')['You are not allowed to create comments on the requested object.']]


        if request.session.mind[CreateCommentPage].creating < 1:
            return ctx.tag[
                ## create-comment form
                tags.div[
                  tags.form(name="createcomment",action="/create/comment",method="post")[
                  tags.fieldset[
                    tags.legend['Comment Title'],
                    tags.input(type='text',_class='text',id='title',name='title',value=request.session.mind[CreateCommentPage].title)],

                  tags.fieldset[
                    tags.legend['Comment Body'],
                    tags.textarea(style='width:100%;',rows=20,cols=80,name='body',id='body')[request.session.mind[CreateCommentPage].body]],
                  tags.input(type='submit',_class='button',name='update',value='preview >>'),
                  tags.input(type='submit',_class='button',name='create',value='create')
                  ]
                ],

                self.preview(request)
              ]

        elif request.session.mind[CreateCommentPage].creating == 1:
            return ctx.tag[
                ## create-comment form
                tags.div[
                  tags.form(name="createcomment",action="/create/comment",method="post")[
                  tags.p['Really create comment?'],
                  tags.input(type='submit',_class='button',name='update',value='keep updating'),
                  tags.input(type='submit',_class='button',name='create',value='yes, create')
                  ]
                ],

                self.preview(request)
              ]

        else:
            rtn = ctx.tag[
                tags.div(_class="help")['You have successfully created comment "',
                                        tags.a(href=makeRelative(request.uri,'/comment')+'/%s'%request.session.mind[CreateCommentPage].commentId)[request.session.mind[CreateCommentPage].commentId],
                                        '". You may wish to ',
                                        tags.a(href=makeRelative(request.uri,request.session.mind[CreateCommentPage].redirect))['view the comment in context'],
                                        '.']
                ]
            del request.session.mind[CreateCommentPage]
            return rtn
            


class CreateUserPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'user-create.html'))
    watermark = 'participate'

    def _makeData(self):
        class data(object):
            creating = 0
            errors = False
            user = None
            name = ''
            username = ''
            email = []
            phone = []
            address = ''
            bio = ''
            private = False
        return data()
    _makeData = classmethod(_makeData)


    def preview(self,request):
        return tags.p['No preview yet']

    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = GenericPage.render_links(self,ctx,data)
        rtn.append(tags.link(rel='stylesheet',href=makeRelative(request.uri,'/activist-form.css')))
        return rtn

    def crumbExtras(self,ctx):
        return [ 'create user' ]



    def beforeRender(self,ctx):
        GenericPage.beforeRender(self,ctx)
        
        request = inevow.IRequest(ctx)
        
        if request.args.has_key('name'):
            request.session.mind[CreateUserPage].name = request.args['name'][0]
        if request.args.has_key('username'):
            request.session.mind[CreateUserPage].username = request.args['username'][0]
        if request.args.has_key('address'):
            request.session.mind[CreateUserPage].address = request.args['address'][0]
        if request.args.has_key('bio'):
            request.session.mind[CreateUserPage].bio = request.args['bio'][0]
        if request.args.has_key('private'):
            request.session.mind[CreateUserPage].private = request.args['private'][0]
        if request.args.has_key('email'):
            request.session.mind[CreateUserPage].email = []
            for x in request.args['email'][0].split('\n'):
                x = x.strip()
                if len(x):
                    request.session.mind[CreateUserPage].email.append(x)
        if request.args.has_key('phone'):
            request.session.mind[CreateUserPage].phone = []
            for x in request.args['phone'][0].split('\n'):
                x = x.strip()
                if len(x):
                    request.session.mind[CreateUserPage].phone.append(x)
                
    def render_form_cb(self,args):
        (page,ctx,person) = args
        request = inevow.IRequest(ctx)
        request.session.mind[CreateUserPage].creating = 2
        return page.render_success(ctx,person)


    def render_success(self,ctx,data):
        request = inevow.IRequest(ctx)
        del request.session.mind[CreateUserPage]
        return ctx.tag[
            tags.div(_class='message')['You have successfully created user "',
                                       tags.a(href=makeURL(request.uri, data))[data.name()],
                                       '". Your password should arrive at the mail address(es) you specified. You may then ',
                                       tags.a(href=makeRelative(request.uri,'/login'))['login'],'.']
            ]
                    
    def render_form(self,ctx,data):
        request = inevow.IRequest(ctx)
        usernameerror = ''
        nameerror = ''
        emailerror = tags.p['Your password will be sent to these email addresses.']

        if len(request.session.mind[CreateUserPage].email) > Config.max_email_addresses:
            emailerror = tags.div(_class='error')['You may only list up to %d email addresses.'%Config.max_email_addresses]
            request.session.mind[CreateUserPage].email = request.session.mind[CreateUserPage].email[:Config.max_email_addresses]
            request.session.mind[CreateUserPage].errors = 1
        
        if request.args.has_key('create'):
            if request.session.mind[CreateUserPage].creating < 1 and not request.session.mind[CreateUserPage].errors:
                request.session.mind[CreateUserPage].creating = 1
                
            elif request.session.mind[CreateUserPage].creating == 1 and not request.session.mind[CreateUserPage].errors:

                from whrandom import choice
                import string
                passwd = ''
                for i in range(8):
                    passwd = passwd + choice(string.letters + string.digits)


                from twisted.mail.smtp import sendmail
                rtn = sendmail(Config.email_host, "newuser@activist.ca", request.session.mind[CreateUserPage].email, """
Greetings,

Someone from IP %(ip)s has requested that a new user
at http://activist.ca be created with this email
address.

Your password is ``%(passwd)s'' for username ``%(username)s''.
If you did not intend to create this account, you may visit:

  http://activist.ca/user/%(username)s

...and log in with the above password and delete
the account. Or ignore this.
""" % {'username':request.session.mind[CreateUserPage].username,
       'passwd':passwd,
       'ip':request.getClientIP()
       } )

                class createperson:
                    def __init__(self,ctx,page):
                        self.ctx = ctx
                        self.page = page
                    def createperson_cb(self,arg):
                        req = inevow.IRequest(self.ctx)
                        (num,addrs) = arg
                        print "IN PERSON CALLBACK:",num,addrs
                        if num > 0:
                            from activist.Person import Person
                            user = Person()
                            user.create( req.session.mind[CreateUserPage].name,
                                         req.session.mind[CreateUserPage].username,
                                         passwd,
                                         req.session.mind[CreateUserPage].phone,
                                         req.session.mind[CreateUserPage].email,
                                         req.session.mind[CreateUserPage].address,
                                         req.session.mind[CreateUserPage].bio,
                                         req.session.mind[CreateUserPage].private)
                            req.session.mind[CreateUserPage].user = user
                            req.session.mind[CreateUserPage].creating = 2
                                        # HACK FIXME TODO
                            if user.username()[:5] == "admin":
                                print "HACK: making user an admin because their name starts with \"admin\""
                                user.make_administrator(user,True)
                                user.make_moderator(user,True)
                            return (self.page,self.ctx,user)
                        raise errors.EmailFailed('Failed to send any emails')
                    
                def render_error_cb(arg):
                    ## FIXME TODO log this error and perhaps auto-send
                    ## a message...although if the local mailer is
                    ## gone (likely cause of this) then that won't
                    ## work, will it? ;)
                    print "FAILURE:",arg,dir(arg)
                    extra = []
                    if isinstance(arg.value,error.ConnectionRefusedError):
                        extra.append(tags.p['This is likely an internal configuration problem (the local mailer is gone); please ',
                                            tags.a(href='mailto:webmaster@activist.ca?subject=%s'%arg.getErrorMessage())['send a message to the webmaster'],
                                            '. Thanks (I\'d do it myself, but my mailer is not around...)'])
                        
                    return tags.div(_class='error')['Could not send mail:',
                                                    tags.pre[arg.getErrorMessage()],
                                                    extra]

                    
                rtn.addCallback(createperson(ctx,self).createperson_cb)
                rtn.addCallback(self.render_form_cb)
                rtn.addErrback(render_error_cb)
                return rtn
                    
                              
        else:
            request.session.mind[CreateUserPage].creating = 0
            
        

        request.session.mind[CreateUserPage].errors = False
        if request.session.mind[CreateUserPage].creating > 0:
            if Users.has_key(request.session.mind[CreateUserPage].username):
                usernameerror = tags.div(_class='error')['Username "' + request.session.mind[CreateUserPage].username + '" already exists.']
                request.session.mind[CreateUserPage].username = ''
                request.session.mind[CreateUserPage].errors = True

            if len(request.session.mind[CreateUserPage].email) == 0:
                emailerror = tags.div(_class='error')['You must enter at least one email; the first one will be used to send your password to.']
                request.session.mind[CreateUserPage].errors = True

        if not request.session.mind[CreateUserPage].errors and request.session.mind[CreateUserPage].creating == 1:
            return ctx.tag[
                tags.div[
                  tags.p[tags.b['name:'],request.session.mind[CreateUserPage].name,tags.br,
                         tags.b['username:'],request.session.mind[CreateUserPage].username,tags.br,
                         tags.b['email:'],','.join(request.session.mind[CreateUserPage].email),tags.br,
                         tags.b['phone:'],','.join(request.session.mind[CreateUserPage].phone),tags.br,
                         tags.b['address:'],request.session.mind[CreateUserPage].address,tags.br,
                         tags.b['bio:'],request.session.mind[CreateUserPage].bio,tags.br,
                         tags.b['private:'],request.session.mind[CreateUserPage].private,tags.br],
                  tags.p['Everything looks okay. Really create user?'],
                  tags.form(name="createuser",action=request.uri,method="post")[
                  tags.input(type='submit',_class='button',name='create',value='create')
                  ]]]

        if request.session.mind[CreateUserPage].private:
            privacy = tags.input(type='checkbox',id='private',name='private',checked='checked')
        else:
            privacy = tags.input(type='checkbox',id='private',name='private')
                
        return ctx.tag[
                tags.div[
                  tags.form(name="createuser",action=request.uri,method="post")[
                  tags.fieldset[
                    tags.legend['Real Name'],
                    nameerror,
                    tags.input(type='text',_class='text',id='name',name='name',value=request.session.mind[CreateUserPage].name)],

                  tags.fieldset[
                    tags.legend['User Name'],
                    usernameerror,
                    tags.input(type='text',_class='text',id='username',name='username',value=request.session.mind[CreateUserPage].username)],

                  tags.fieldset[
                    tags.legend['Privacy'],
                    tags.p['If you check this, you will not appear in listings of users and nobody except administrators will be able to view your information (although your username will still be connected with listings you create).'],
                    tags.label[privacy,'Keep me private']
                  ],

                  tags.fieldset[
                    tags.legend['Biography'],
                    usernameerror,
                    tags.textarea(style='width:100%;',rows=5,cols=80,id='bio',name='bio',value=request.session.mind[CreateUserPage].bio)],

                  tags.fieldset[
                    tags.legend['Address'],
                    tags.textarea(style='width:100%;',rows=3,cols=80,name='address',id='address')[request.session.mind[CreateUserPage].address]],
                  
                  tags.fieldset[
                    tags.legend['Email Address(es)'],
                    emailerror,
                    tags.textarea(style='width:100%;',rows=3,cols=80,name='email',id='email')['\n'.join(request.session.mind[CreateUserPage].email)]],
                  
                  tags.fieldset[
                    tags.legend['Phone Number(s)'],
                    tags.textarea(style='width:100%;',rows=3,cols=80,name='phone',id='phone')['\n'.join(request.session.mind[CreateUserPage].phone)]],
                  
                  tags.input(type='submit',_class='button',name='create',value='create')
                  ]
                ]
              ]

class CreateMediaPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'media-create.html'))
    watermark = 'participate'

    def _makeData(self):
        class data(object):
            creating = 0
            filedata = None
            filename = None
            alias = ''
        return data()
    _makeData = classmethod(_makeData)

    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = GenericPage.render_links(self,ctx,data)
        rtn.append(tags.link(rel='stylesheet',href=makeRelative(request.uri,'/activist-form.css')))
        return rtn

    def crumbExtras(self,ctx):
        return [ ]

    def render_form(self,ctx,data):
        request = ctx.locate(inevow.IRequest)

        if not request.session.mind.canCreateMedia():
            return ctx.tag[tags.div(_class='warning')['You are not allowed to upload media.']]

        file = None
        if request.args.has_key('file'):
            file = request.fields['file']
            request.session.mind[CreateMediaPage].filename = file.filename
            request.session.mind[CreateMediaPage].filedata = file.value
            request.session.mind[CreateMediaPage].alias = request.args['alias'][0]
            request.session.mind[CreateMediaPage].creating = 1

        if request.session.mind[CreateMediaPage].creating < 1:
            return ctx.tag[
                tags.div[
                tags.form(action="/create/media",method="post",enctype="multipart/form-data")[
                  tags.p['Upload media:'],
                    tags.input(type='file',name='file'),tags.br,
                  tags.label(_for='alias')['Alias:'], tags.input(type='text',name='alias'),tags.br,
                    tags.input(type='submit',_class='button',name='upload',value='upload')]
                ]]

        else:
            CommandProcessor.assertConsistency()
            
            media = activist.Media.Media()
            media.create(request.session.mind[CreateMediaPage].filename,
                         request.session.mind[CreateMediaPage].filedata,
                         creator=request.session.mind.person())
            aliastext = request.session.mind[CreateMediaPage].alias.strip()
            if len(aliastext):
                for oldalias in DataStore.aliases():
                    if oldalias._origin == aliastext:
                        print "DELETING alias:",oldalias
                        oldalias.delete(request.session.mind.person())
                alias = activist.Alias.Alias()
                alias.create(aliastext,'/media/'+str(media.id()))

            CommandProcessor.assertConsistency()
            
            rtn = ctx.tag[
                tags.div[
                  tags.p(style='font: fixed;')['Uploaded file with ',
                                               tags.span(style='font-weight:bold;')[str(len(request.session.mind[CreateMediaPage].filedata))+' bytes'],
                                               ' and name ',
                                               tags.a(href=makeRelative(request.uri,'/media')+'/%s'%media.id(),style='font-weight:bold;')[request.session.mind[CreateMediaPage].filename],
                                               ' with alias: ',
                                               tags.a(href=makeRelative(request.uri,aliastext))[aliastext]],
                  tags.p[tags.a(href=makeRelative(request.uri,'/create/media'))['Upload more'],'?']
                  ]
                ]
            request.session.mind[CreateMediaPage].filename = ''
            request.session.mind[CreateMediaPage].filedata = None
            request.session.mind[CreateMediaPage].creating = 0
            return rtn
            
        
class CreateGroupPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'group-create.html'))
    watermark = 'participate'
    
    def _makeData(self):
        class data(object):
            editing = None
            creating = 0
            preview = 1
            title = 'Group Title'
            url = 'http://www.example.com'
            brief = 'Brief (2-3 sentences maximum) version of the description.'
            text = '''Group description. This uses reStructuredText, and can have *italic* or **bold text** easily\ninserted, along with `named links`_ and many other things; see the tutorial above.URLs like\nhttp://www.example.com are made into links. No HTML, please! (Or it <b>will</b> look <i>stupid</i>...)\n\n.. _named links: http://www.example.com\n\nThis Is A Subtitle\n------------------\n\nIf your description is quite long, you might want subtitles.'''
        return data()
    _makeData = classmethod(_makeData)


    def beforeRender(self,ctx):
        request = inevow.IRequest(ctx)
        if request.args.has_key("edit"):
            try:
                edit = DataStore[request.args['edit'][0]]
                request.session.mind[CreateGroupPage].editing = edit

                request.session.mind[CreateGroupPage].creating = 0
                request.session.mind[CreateGroupPage].preview = 1
                request.session.mind[CreateGroupPage].title = edit.info()._name
                request.session.mind[CreateGroupPage].url = edit.info()._url
                request.session.mind[CreateGroupPage].brief = edit.info()._brief
                request.session.mind[CreateGroupPage].text = edit.info()._description
                print "EDITING!",edit.info()._name,edit.info()._url,edit.info()._brief,edit.info()._description
                
            except KeyError:
                pass

    def preview(self,request):
        if request.session.mind[CreateGroupPage].preview == 0:
            return ''

        return tags.div(id="preview",style="z-index:-5;")[
          tags.div(_class='listing')[
            tags.div(_class='listingbody')[
            tags.span(_class='url')[tags.a(href=request.session.mind[CreateGroupPage].url)[request.session.mind[CreateGroupPage].url]],
              tags.h1[request.session.mind[CreateGroupPage].title],
              tags.xml(activist.text.ToHTML(request.session.mind[CreateGroupPage].text))
            ]
          ]
        ]
        
    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = GenericPage.render_links(self,ctx,data)
        rtn.append(tags.link(rel='stylesheet',href=makeRelative(request.uri,'/activist-form.css')))
        return rtn

    def render_form(self,ctx,data):
        request = inevow.IRequest(ctx)
        d = request.session.mind[CreateGroupPage]
        print "DATA:"
        for x in dir(d):
            print "  ",str(getattr(d,x))
        if request.session.mind[CreateGroupPage].creating == 1:
            return ctx.tag[
                  ## "really create?"
                  tags.div[
                    tags.form(action="/create/group",method="post")[
                    tags.p['Really create Group listing?'],
                    tags.input(type='submit',name='create',value='create',_class='button'),
                    tags.input(type='submit',name='no',value='keep updating',_class='button')
                    ]
                  ],

                  ## preview
                  self.preview(request)
                ]

        elif request.session.mind[CreateGroupPage].creating == 2:
            extra = []
            if hasattr(request.session.mind[CreateEventPage],'diverged'):
                del request.session.mind[CreateEventPage].diverged
                extra = [ tags.p['You may also wish to ', tags.a(href=makeRelative(request.uri,'/event/create'),style='font-weight:bold;font-size:120%;')['continue with creating your event'], '.'] ]
            rtn = ctx.tag[
                tags.div(_class='help')[
                  tags.p['You have successfully created a new group with ID: ',
                         tags.a(href=makeRelative(request.uri,'/group')+'/%s'%request.session.mind[CreateGroupPage].lastid)[request.session.mind[CreateGroupPage].lastid],
                         '. Your group will not be visible in the group lists until a moderator reviews your listing [',
                         tags.a(href=makeRelative(request.uri,'/help/moderators'))['explain'],
                         '].  If you like, you may ',
                         tags.a(href=makeRelative(request.uri,'/group/create'))['create another'],
                         '.', extra]
                  ]]
            del request.session.mind[CreateGroupPage]
            return rtn

        return ctx.tag[
            ## create-group form
            tags.div[
              tags.form(name="creategroup",action=request.uri,method="post")[
              tags.fieldset[
                tags.legend['Group Title'],
                tags.input(type='text',_class='text',size='80',id='title',name='title',value=request.session.mind[CreateGroupPage].title)],

              tags.fieldset[
                tags.legend['Group URL'],
                tags.input(type='text',_class='text',size='80',id='url',name='url',value=request.session.mind[CreateGroupPage].url)],

              tags.fieldset[
                tags.legend['Group Description'],
                
                tags.fieldset[
                  tags.legend['Full Description'],
                  tags.textarea(style='width:100%;',rows=20,cols=80,name='text',id='text')[request.session.mind[CreateGroupPage].text]],
              
                tags.fieldset[
                  tags.legend['Brief'],
                  tags.textarea(style='width:100%;',rows=4,cols=80,name='brief',id='brief')[request.session.mind[CreateGroupPage].brief]],
                ],
              
              tags.input(type='submit',name='preview',value='preview >>',_class='button'),
              tags.input(type='submit',name='create',value='create',_class='button')
              ]
            ],

            self.preview(request)
          ]

class PersonListPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'person-list.html'))
    watermark = 'event'

    def crumbExtras(self,ctx):
        return [ tags.a(href=makeRelative(request.uri,'/person/'))['people'] ]

    def data_people(self,ctx,data):
        for x in DataStore.getByClass(activist.Person.Person):
            if not x.private(): yield x
        return
    #return filter(lambda x: not x.private(), DataStore.getByClass(activist.Person.Person))


class GroupListPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'group-list.html'))
    watermark = 'event'

    def crumbExtras(self,ctx):
        return [ tags.a(href=makeRelative(request.uri,'/group/'))['groups'] ]

    def data_groups(self,ctx,data):
        return DataStore.getByClass(activist.Group.Group)


## FIXME this should use view stuff (IFullView...)
class GroupPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'group-fullview-page.html'))
    watermark = 'event'

    def beforeRender(self,ctx):
        GenericPage.beforeRender(self,ctx)
        
        from activist.Group import Group
        request = inevow.IRequest(ctx)
        if request.args.has_key('joingroup'):
            del request.args['joingroup']
            if isinstance(request.session.mind.object,Group):
                request.session.mind.object.person(request.session.mind.person())
            else:
                print "   not group:",request.session.mind.object

        elif request.args.has_key('unjoingroup'):
            if request.session.mind.person():
                try:
                    target = DataStore[request.args['unjoingroup'][0]]
                    if request.session.mind.canAdmin() or request.session.mind.person() == target:
                        request.session.mind.object.unperson(target,request.session.mind.person())
                except KeyError:
                    pass
            del request.args['unjoingroup']
            
        
    def crumbExtras(self,ctx):
        return [ tags.a(href=makeRelative(request.uri,'/group/'))['groups'],
                 ' < ',
                 inevow.IRequest(ctx).session.mind.object.info()._name ]

    def data_group(self,ctx,data):
        return inevow.IRequest(ctx).session.mind.object

    ## see view.py; all the action is there

class CreateEventPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'event-create.html'))
    watermark = 'participate'
   
    def _makeData(self):
        class data(object):
            error = None
            
            editing = None
            creating = 0
            date = activist.Time.TimeRange(time.time(),2*3600)
            dateerror = None
            title = 'Event Title'
            placeerror = None
            place = {'name':'',
                     'phone':'',
                     'address':'',
                     'access':[],
                     'locale':'calgary.ab.ca',
                     'notes':'',
                     'maplink':''}
            group = None
            listingtype = 'other'
            categories = []
            access = []
            contacts = []
            preview = 0
            brief = 'Brief (2-3 sentences maximum) version of the description.'
            text = '''Event description. This uses reStructuredText, and can have *italic* or **bold text** easily\ninserted, along with `named links`_ and many other things; see the tutorial above.URLs like\nhttp://www.example.com are made into links. No HTML, please! (Or it <b>will</b> look <i>stupid</i>...)\n\n.. _named links: http://www.example.com\n\nThis Is A Subtitle\n------------------\n\nIf your description is quite long, you might want subtitles.'''
        return data()
    _makeData = classmethod(_makeData)

    def beforeRender(self,ctx):
        GenericPage.beforeRender(self,ctx)
        
        request = inevow.IRequest(ctx)

        if request.args.has_key("edit"):
            try:
                edit = DataStore[request.args['edit'][0]]
                request.session.mind[CreateEventPage].editing = edit
                request.session.mind[CreateEventPage].title = edit.info().title()
                request.session.mind[CreateEventPage].brief = edit.info().brief()
                request.session.mind[CreateEventPage].text = edit.info().description()
                request.session.mind[CreateEventPage].date = edit.info().whenRaw()
                request.session.mind[CreateEventPage].contacts = edit.info().contacts()

                place = edit.info().where()
                if place:
                    request.session.mind[CreateEventPage].place = {
                        'name': place._name,
                        'address': place._address,
                        'phone': place._phone,
                        'locale': place._locale,
                        'notes': place._notes,
                        'maplink': place._maplink }
                    
                if len(edit.groups()):
                    request.session.mind[CreateEventPage].group = edit.groups()[0]
                request.session.mind[CreateEventPage].categories = []
                for cat in edit.categories():
                    request.session.mind[CreateEventPage].categories.append(cat)
            except KeyError:
                pass

        if request.args.has_key("preview") or request.args.has_key("create"):
            request.session.mind[CreateEventPage].preview = 1

        try:
            newtime = activist.Time.fromEventArgs(request.args)
            if newtime:
                request.session.mind[CreateEventPage].date = newtime
        except errors.TimeParseError,e:
            request.session.mind[CreateEventPage].dateerror = str(e)
            print "TIME ERROR:",e


        for key in request.args.keys():
            if key[:6] == 'place_':
                request.session.mind[CreateEventPage].place[key[6:]] = request.args[key][0]

        if request.args.has_key('clear_placeaccess'):
            request.session.mind[CreateEventPage].place['access'] = []
        for k in activist.EventInformation.ACCESSIBLE_KEYS:
            if request.args.has_key('placeaccess_' + k):
                request.session.mind[CreateEventPage].place['access'].append(k)

        ## this must come after all the place stuff up there
        maybeplace = DataStore.getPlaceByName(request.session.mind[CreateEventPage].place['name'])
        if maybeplace:
            d = request.session.mind[CreateEventPage].place
            d['phone'] = maybeplace._phone
            d['address'] = maybeplace._address
            d['locale'] = maybeplace._locale
            d['notes'] = maybeplace._notes
            d['maplink'] = maybeplace._maplink
            d['place_id'] = maybeplace.id()

        if request.args.has_key('clear_eventaccess'):
            request.session.mind[CreateEventPage].access = []
        for k in activist.EventInformation.ACCESSIBLE_KEYS:
            if request.args.has_key('eventaccess_' + k):
                request.session.mind[CreateEventPage].access.append(k)

        if request.session.mind.person() and request.args.has_key('selfcontact') and request.args['selfcontact'][0] == 'on':
            if not request.session.mind.person() in request.session.mind[CreateEventPage].contacts:
                request.session.mind[CreateEventPage].contacts.append(request.session.mind.person())

        if request.args.has_key('othercontacts'):
            for x in request.args['othercontacts'][0].split():
                if Users.has_key(x):
                    (passwdhash,pid) = Users[x]
                    p = DataStore[pid]
                    if not p in request.session.mind[CreateEventPage].contacts:
                        request.session.mind[CreateEventPage].contacts.append(p)
                

        if request.args.has_key('title'):
            request.session.mind[CreateEventPage].title = request.args['title'][0]
        if request.args.has_key('listing_type'):
            request.session.mind[CreateEventPage].listingtype = request.args['listing_type'][0]
        if request.args.has_key('text'):
            request.session.mind[CreateEventPage].text = request.args['text'][0]
        if request.args.has_key('brief'):
            request.session.mind[CreateEventPage].brief = request.args['brief'][0]
        if request.args.has_key('group'):
            if request.args['group'][0] == 'nogroup':
                request.session.mind[CreateEventPage].group = None
            else:
                request.session.mind[CreateEventPage].group = DataStore[request.args['group'][0]]

        for (key,value) in request.args.items():
            if key[:8] == 'category':
                cat = DataStore[value[0]]
                if not cat in request.session.mind[CreateEventPage].categories:
                    request.session.mind[CreateEventPage].categories.append(cat)
            
        if request.args.has_key('no'):
            request.session.mind[CreateEventPage].creating = 0
                
        elif request.args.has_key('create'):
            if request.session.mind[CreateEventPage].creating < 1:
                if self.validate(ctx):
                    request.session.mind[CreateEventPage].creating = 1
            else:
                CommandProcessor.assertConsistency()
                
                creator = request.session.mind.person()
                title = request.session.mind[CreateEventPage].title
                when = request.session.mind[CreateEventPage].date
                text = request.session.mind[CreateEventPage].text
                brief = request.session.mind[CreateEventPage].brief
                group = request.session.mind[CreateEventPage].group
                access = request.session.mind[CreateEventPage].access
                listingtype = request.session.mind[CreateEventPage].listingtype
                contacts = request.session.mind[CreateEventPage].contacts

                placedict = request.session.mind[CreateEventPage].place
                if placedict.has_key('place_id'):
                    place = DataStore[placedict['place_id']]
                    
                else:
                    place = activist.Place.Place()
                    place.create(placedict['name'],
                                 placedict['address'],
                                 placedict['phone'],
                                 placedict['locale'],
                                 placedict['notes'],
                                 placedict['maplink'])
                
                if request.session.mind[CreateEventPage].editing:
                    event = request.session.mind[CreateEventPage].editing
                    approve = False
                    if request.session.mind.canAdmin() and event.isApproved():
                        approve = True
                    event.edit(title,when,access,text,brief,place,listingtype,contacts,creator)
                    request.session.mind[CreateEventPage].creating = 3
                    if approve: event.info().approve(request.session.mind.person())
                else:
                    event = activist.Event.Event()
                    event.create(creator,title,when,access,text,brief,place,listingtype,contacts)
                    request.session.mind[CreateEventPage].creating = 2
                    if request.session.mind.canModerate(event):
                        event.info().approve(request.session.mind.person())
                    
                for cat in request.session.mind[CreateEventPage].categories:
                    event.category(cat)
                if group:
                    group.event(event)

                   
                request.session.mind[CreateEventPage].lastid = event.id()
                CommandProcessor.assertConsistency()

    def validate(self,ctx):
        request = inevow.IRequest(ctx)
        request.session.mind[CreateEventPage].error = None

# !MW! what is this for? FIXME
#        if request.session.mind[CreateEventPage].place.has_key('place_id'):
#            return True

        
        place = request.session.mind[CreateEventPage].place
        error = []
        if place['name'] == '':
            error = error + [tags.p['You have not entered a place name.']]
        if place['phone'] == '':
            error = error + [tags.p['You have not entered a place phone number.']]
        if place['address'] == '':
            error += [tags.p['You have not entered a place address.']]

        if request.args.has_key('othercontacts'):
            for x in request.args['othercontacts'][0].split():
                if not Users.has_key(x):
                    error += [tags.p['Cannot find username "%s"' % x]]
                    print "OH NO!",x

        request.session.mind[CreateEventPage].contacterror = ''            
        if len(error):
            request.session.mind[CreateEventPage].error = error
            return False
        return True
            

    def renderHTTP(self,ctx):
        request = inevow.IRequest(ctx)

        if request.args.has_key('cancel'):
            if request.session.mind[CreateEventPage].editing:
                request.redirect(makeURL(request.uri, request.session.mind[CreateEventPage].editing))
            del request.session.mind[CreateEventPage]
            return
        
        if request.args.has_key('creategroup'):
            request.session.mind[CreateEventPage].diverged = True
            self.beforeRender(ctx)
            request.args.clear()
            request.redirect('/create/group')
            return None
        
        return super(CreateEventPage,self).renderHTTP(ctx)


    def preview(self,request):
        if request.session.mind[CreateEventPage].preview == 0:
            return ''

        return tags.div(id="preview",style="z-index:-5;")[
          tags.div(_class='listing')[
            tags.div(_class='listingbody')[
              tags.h1[request.session.mind[CreateEventPage].title],
              tags.p(_class='date')[request.session.mind[CreateEventPage].date.format('%A, %B %d %Y @ %H:%M %Z')],
              tags.xml(activist.text.ToHTML(request.session.mind[CreateEventPage].text))
            ]
          ]
        ]

    def render_title(self,ctx,data):
        return ctx.tag
        
    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = GenericPage.render_links(self,ctx,data)
        rtn.append(tags.link(rel='stylesheet',href=makeRelative(request.uri,'/activist-form.css')))
        return rtn

    def render_editing(self,ctx,data):
        request = inevow.IRequest(ctx)
        if request.session.mind[CreateEventPage].editing:
            if request.session.mind.isAnonymous():
                return ctx.tag['You are trying to edit "', tags.a(href=makeURL(request.uri, request.session.mind[CreateEventPage].editing))[request.session.mind[CreateEventPage].editing.info().title()],'", but you are not ',tags.a(href=makeRelative(request.uri,'/login')+'?redirect=/create/event')['logged in'],'.']
            else:
                return ctx.tag['You are editing: "', tags.a(href=makeURL(request.uri, request.session.mind[CreateEventPage].editing))[request.session.mind[CreateEventPage].editing.info().title()],'".']
        return ''

    def render_warnings(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = []
        if request.session.mind.isAnonymous():
            rtn = rtn + ['WARNING: you are not ',
                         tags.a(href=makeRelative(request.uri,'/login')+'?redirect=/event/create')['logged in'],
                         '. If you were, you would be able to edit your event later, associate groups to this event and become a contact (if you like).']

        if request.session.mind[CreateEventPage].error:
            rtn = rtn + [request.session.mind[CreateEventPage].error]

        if len(rtn):
            return ctx.tag[rtn]
        return ''

    def render_form(self,ctx,data):
        request = inevow.IRequest(ctx)

        if request.session.mind[CreateEventPage].date is None:
            raise "DANGER!"

        if request.session.mind[CreateEventPage].editing and request.session.mind.isAnonymous():
            return ctx.tag[ tags.div(_class='error')['You must be logged in to edit an event.']]
        
        createoredit = 'create'
        if request.session.mind[CreateEventPage].editing: createoredit = 'edit'
        if request.session.mind[CreateEventPage].creating == 1:
            return ctx.tag[
                  ## "really create?"
                  tags.div[
                    tags.form(action="/create/event",method="post")[
                    tags.p['Really create Event listing?'],
                    tags.input(type='submit',name='create',value=createoredit,_class='button'),
                    tags.input(type='submit',name='no',value='keep updating',_class='button'),
                    tags.input(type='submit',name='cancel',value='cancel',_class='button')
                    ]
                  ],

                  ## preview
                  self.preview(request)
                ]

        elif request.session.mind[CreateEventPage].creating == 2:
            ev = DataStore[request.session.mind[CreateEventPage].lastid]
            rtn = ctx.tag[
                tags.div(_class='help')[
                  tags.p['You have successfully created a new event ("',
                         tags.a(style='font-weight:bold;',href=makeURL(request.uri, ev))[ev.info().title()],'") with ID: ',
                         tags.a(href=makeURL(request.uri, ev))[ev.id()],
                         '. Unless you are allowed to moderate this event, your event will not be visible in the event lists until a moderator reviews your listing [',
                         tags.a(href=makeRelative(request.uri,'/help/moderators'))['explain'],
                         '].  If you like, you may ',
                         tags.a(href=makeRelative(request.uri,'/event/create'))['create another'],
                         '.']
                  ]]
            del request.session.mind[CreateEventPage]
            return rtn

        elif request.session.mind[CreateEventPage].creating == 3:
            if request.session.mind.canModerate(request.session.mind[CreateEventPage].editing):
                approved = ['Since you can moderate this event, it is now automatically approved in its edited state.']
                request.session.mind[CreateEventPage].editing.info().approve(request.session.mind.person())
            else:
                approved = [ 'Your event will now no longer appear in the event lists until a moderator has reviewed your edits [',
                             tags.a(href=makeRelative(request.uri,'/help/approval'))['explain'],
                             '].' ]

            rtn = ctx.tag[
                tags.div(_class='help')[
                  tags.p['You have successfully edited event "',
                         tags.a(href=makeRelative(request.uri,'/event')+'/%s'%request.session.mind[CreateEventPage].lastid)[request.session.mind[CreateEventPage].editing.info().title()],
                         '". ',
                         approved
                         ]
                  ]]
            del request.session.mind[CreateEventPage].editing
            del request.session.mind[CreateEventPage]
            return rtn

        categories = []
        for x in DataStore.getCategories():
            if x in request.session.mind[CreateEventPage].categories:
                categories.append(tags.label(_for=x.id())[tags.input(_type='checkbox',checked='checked',name='category_'+x.id(),id=x.id(),value=x.id()),x.title(),' '])
            else:
                categories.append(tags.label(_for=x.id())[tags.input(_type='checkbox',name='category_'+x.id(),id=x.id(),value=x.id()),x.title(),' '])

        group = []
        if request.session.mind.person():
            group.append( tags.input(_type='radio',name='group',id='nogroup',value='nogroup',checked='checked')[tags.label(_for='nogroup')['No group'],' '] )
            for gr in request.session.mind.person().groups():
                group.append( tags.input(_type='radio',name='group',id=gr.id(),value=gr.id())[tags.label(_for=gr.id())[gr.info()._name],' '] )
            group.append( tags.input(_class='inlinebutton',type='submit',value='Create New Group',name='creategroup',id='creategroup') )
        else:
            group.append( tags.div(_class='important')['You must logged in and belong to one or more groups to associate events to a Group.'] )

        placedict = request.session.mind[CreateEventPage].place
        place_locale = tags.select(name='place_locale',id='place_locale')
        for a in activist.Place.LOCALES:
            if placedict['locale'] == a[0]:
                place_locale = place_locale[ tags.option(selected='selected',name=a[0],value=a[0])[a[1]] ]
            else:
                place_locale = place_locale[ tags.option(name=a[0],value=a[0])[a[1]] ]

        event_access = [tags.input(_type='hidden',name='clear_eventaccess',value='true')]
        for a in activist.EventInformation.ACCESSIBLE_KEYS:
            if a in request.session.mind[CreateEventPage].access:
                event_access.append( tags.label[tags.input(_type='checkbox',checked='checked',name='eventaccess_'+a,id='eventaccess_'+a),' ',
                                                activist.EventInformation.KEY_TO_DESCRIPTION[a]] )
            else:
                event_access.append( tags.label[tags.input(_type='checkbox',name='eventaccess_'+a,id='eventaccess_'+a),' ',
                                                activist.EventInformation.KEY_TO_DESCRIPTION[a]] )
            event_access.append(tags.br)

        listingtypes = []
        for a in activist.EventInformation.TYPES:
            if a == request.session.mind[CreateEventPage].listingtype:
                listingtypes.append( tags.option(selected='selected',value=a)[a] )
            else:
                listingtypes.append( tags.option(value=a)[a] )

        dateerror = ''
        if request.session.mind[CreateEventPage].dateerror:
            dateerror = tags.div(_class='error')[request.session.mind[CreateEventPage].dateerror]

                                 
        placetip = tags.p(_class='tip')['Try entering just a name and pressing "preview" below; if the place already exists, the rest will be filled it.']
        if request.session.mind[CreateEventPage].place.has_key('place_id'):
            placetip = tags.p(_class='tip')['I am re-using a place with ID ',
                                            tags.a(href=makeRelative(request.uri,'/place/')+request.session.mind[CreateEventPage].place['place_id'])[request.session.mind[CreateEventPage].place['place_id']]]

                                        #
                                        # date stuff
                                        #

        datetype = 'once'
        if isinstance(request.session.mind[CreateEventPage].date,activist.Time.Repeating):
            datetype = 'recurring'
                                        
        date_once = tags.fieldset
        if datetype == 'once':
            date_once = date_once[tags.input(_type='radio',name='radio_date',id='radio_once',value='once',checked='checked')]
        else:
            date_once = date_once[tags.input(_type='radio',name='radio_date',id='radio_once',value='once')]
            
        date_once = date_once[   tags.label(_for='radio_once')["One time only"],
                     tags.div[
                       tags.label(_for='date_once')['date'],
                       tags.input(type='text',_class='text',id='date_once',name='date_once',value=request.session.mind[CreateEventPage].date.format('%B %d %Y'),onmousedown='document.createevent.radio_date[0].checked=true;'),
                       tags.label(_for='time_once')[' ',tags.acronym(title='Hour:Minute in 24-hour format')['time']],
                       tags.input(type='text',_class='text',id='time_once',size='6',name='time_once',value=request.session.mind[CreateEventPage].date.format('%H:%M'),onmousedown='document.createevent.radio_date[0].checked=true;'),
                       tags.label(_for='duration_once')[' ',tags.acronym(title='"six hours", "all day", etc.')['duration']],
                       tags.input(type='text',_class='text',id='duration_once',name='duration_once',value=request.session.mind[CreateEventPage].date.duration(),onmousedown='document.createevent.radio_date[0].checked=true;')
                     ]
                   ]

        date_recurring = tags.fieldset
        if datetype != 'once':
            mydate = request.session.mind[CreateEventPage].date
            date_recurring = date_recurring[tags.input(_type='radio',name='radio_date',id='radio_recurring',value='recurring',checked='checked')]
            date_recurring = date_recurring[tags.label(_for='radio_recurring')["Recurring"]]
            d = tags.select(name='recurring_month',onmousedown='document.createevent.radio_date[1].checked=true;')
            
            if mydate.isWeekly():
                d[tags.option(value='every',selected='selected')['Every...']]
                d[tags.option(value='monthly')['Every month on the...']]
            else:
                d[tags.option(value='every')['Every...']]
                d[tags.option(value='monthly',selected='selected')['Every month on the...']]
                
            date_recurring = date_recurring[tags.div[d]]
            
            date_recurring = date_recurring[
                             tags.select(name='recurring_monthrepeat',onmousedown='document.createevent.radio_date[1].checked=true;')[
                               tags.option(value='nothing',**mydate._method.ifSkip(0))[''],
                               tags.option(value='first',**mydate._method.ifSkip(1))['first'],
                               tags.option(value='second',**mydate._method.ifSkip(2))['second'],
                               tags.option(value='third',**mydate._method.ifSkip(3))['third'],
                               tags.option(value='fourth',**mydate._method.ifSkip(4))['fourth'],
                               tags.option(value='fifth',**mydate._method.ifSkip(5))['fifth']
                             ],
                             tags.select(name='recurring_day',onmousedown='document.createevent.radio_date[1].checked=true;')[
                               tags.option(value='monday',**mydate._method.ifDay(0))['Monday'],
                               tags.option(value='tuesday',**mydate._method.ifDay(1))['Tuesday'],
                               tags.option(value='wednesday',**mydate._method.ifDay(2))['Wednesday'],
                               tags.option(value='thursday',**mydate._method.ifDay(3))['Thursday'],
                               tags.option(value='friday',**mydate._method.ifDay(4))['Friday'],
                               tags.option(value='saturday',**mydate._method.ifDay(5))['Saturday'],
                               tags.option(value='sunday',**mydate._method.ifDay(6))['Sunday']
                             ],
                             tags.label(_for="recurring_time")[' ',tags.acronym(title="Hours:Minutes in 24-hour format.")['at'],' '],
                             tags.input(type='text',_class='text',id='recurring_time',name='recurring_time',value=request.session.mind[CreateEventPage].date.format('%H:%M'),
                                        onmousedown='document.createevent.radio_date[1].checked=true;'),
                             tags.label(_for='recurring_duration')[' ',tags.acronym(title='"six hours", "all day", etc.')['for'],' '],
                             tags.input(type='text',_class='text',id='recurring_duration',name='recurring_duration',value=mydate.duration(),onmousedown='document.createevent.radio_date[1].checked=true;'),

                             tags.br, tags.br,

                             tags.label(_for='recurring_start')['First event occurs on:'],
                             tags.input(type='text',_class='text',id='recurring_start',name='recurring_start',value=time.strftime('%B %d, %Y',time.localtime(mydate.start())),onmousedown='document.createevent.radio_date[1].checked=true;'),
                             
                             tags.label(_for='recurring_stop')['Last event occurs on:'],
                             tags.input(type='text',_class='text',id='recurring_stop',name='recurring_stop',value=time.strftime('%B %d, %Y',time.localtime(mydate.stop())),onmousedown='document.createevent.radio_date[1].checked=true;')
                             ]

        else:
            date_recurring = date_recurring[tags.input(_type='radio',name='radio_date',id='radio_recurring',value='recurring')]
            date_recurring = date_recurring[
                           tags.label(_for='radio_recurring')["Recurring"],
                           tags.div[
                             tags.select(name='recurring_month',onmousedown='document.createevent.radio_date[1].checked=true;')[
                               tags.option(value='every',selected='selected')['Every...'],
                               tags.option(value='monthly')['Every month on the...']
                             ],
                             tags.select(name='recurring_monthrepeat',onmousedown='document.createevent.radio_date[1].checked=true;')[
                               tags.option(value='nothing',selected='selected')[''],
                               tags.option(value='first')['first'],
                               tags.option(value='second')['second'],
                               tags.option(value='third')['third'],
                               tags.option(value='fourth')['fourth'],
                               tags.option(value='fifth')['fifth']
                             ],
                             tags.select(name='recurring_day',onmousedown='document.createevent.radio_date[1].checked=true;')[
                               tags.option(value='sunday')['Sunday'],
                               tags.option(value='monday')['Monday'],
                               tags.option(value='tuesday')['Tuesday'],
                               tags.option(value='wednesday')['Wednesday'],
                               tags.option(value='thursday')['Thursday'],
                               tags.option(value='friday')['Friday'],
                               tags.option(value='saturday')['Saturday']
                             ],
                             tags.label(_for="recurring_time")[' ',tags.acronym(title="Hours:Minutes in 24-hour format.")['at'],' '],
                             tags.input(type='text',_class='text',id='recurring_time',name='recurring_time',value=request.session.mind[CreateEventPage].date.format('%H:%M'),
                                        onmousedown='document.createevent.radio_date[1].checked=true;'),
                             tags.label(_for='recurring_duration')[' ',tags.acronym(title='"six hours", "all day", etc.')['for'],' '],
                             tags.input(type='text',_class='text',id='recurring_duration',name='recurring_duration',value=request.session.mind[CreateEventPage].date.duration(),onmousedown='document.createevent.radio_date[1].checked=true;'),
                             tags.br, tags.br,

                             tags.label(_for='recurring_start')['First event occurs on:'],
                             tags.input(type='text',_class='text',id='recurring_start',name='recurring_start',value=time.strftime('%B %d, %Y',time.localtime(request.session.mind[CreateEventPage].date.start())),onmousedown='document.createevent.radio_date[1].checked=true;'),
                             
                             tags.label(_for='recurring_stop')['Last event occurs on:'],
                             tags.input(type='text',_class='text',id='recurring_stop',name='recurring_stop',value=time.strftime('%B %d, %Y',time.localtime(request.session.mind[CreateEventPage].date.stop())),onmousedown='document.createevent.radio_date[1].checked=true;')
                             ]
                           ]


        contacts = []
        if request.session.mind.person():
                                        # if new event or current user is in the contats, check the "i am a contact" box
            if request.session.mind.person() in request.session.mind[CreateEventPage].contacts or request.session.mind[CreateEventPage].creating == 0:
                contacts.append(tags.div[tags.label(_for='selfcontact')[
                    tags.input(type='checkbox',id='selfcontact',name='selfcontact',checked='checked'),
                    'I am a contact for this event.']])
            else:
                contacts.append(tags.div[tags.label(_for='selfcontact')[
                    tags.input(type='checkbox',id='selfcontact',name='selfcontact'),
                    'I am a contact for this event.']])

        else:
            contacts.append( tags.div(_class='important')['If you were logged in, you could make yourself a contact for this event.'] )

        othercontactvalue = ''
        for x in request.session.mind[CreateEventPage].contacts:
            if x == request.session.mind.person():
                continue
            username = Users.findUsername(x)
            othercontactvalue = othercontactvalue + str(username) + ' '
        contacts.append(tags.div[tags.label(_for='othercontacts')[
            tags.fieldset[
              tags.legend['Other Contacts'],
              tags.label['Usernames (space-separated):',
                         tags.input(type='text',id='othercontacts',name='othercontacts',size='80',_class='text',value=othercontactvalue)]
              ]]])
        
        contacts.append(
            tags.fieldset[
              tags.legend['Create New Contact'],
              tags.label['Name:',tags.input(type='text',id='newcontact_name',name='newcontact_name',size=40)],
              tags.label['Phone 1:',tags.input(type='text',id='newcontact_phone0',name='newcontact_phone0',size=40)],
              tags.label['Phone 2:',tags.input(type='text',id='newcontact_phone1',name='newcontact_phone1',size=40)],
              tags.label['Email:',tags.input(type='text',id='newcontact_email',name='newcontact_email',size=40)]
            ] )
              


        return ctx.tag[
            ## create-event form
            tags.div[
              tags.form(name="createevent",action=request.uri,method="post")[
              tags.input(type='submit',name='preview',value='preview >>',_class='button'),
              tags.input(type='submit',name='create',value=createoredit,_class='button'),
              tags.input(type='submit',name='cancel',value='cancel',_class='button'),
              
              tags.fieldset[
                tags.legend['Event Title'],
                tags.input(type='text',_class='text',size='80',id='title',name='title',value=request.session.mind[CreateEventPage].title)],

              tags.fieldset[
                tags.legend['Categories'],
                tags.fieldset[
                  categories
                ]
              ],

              tags.fieldset[
                tags.legend['Listing Type'],
                tags.select(name='listing_type',id='listing_type')[
                  listingtypes
                ]
              ],

              tags.fieldset[
                tags.legend['Group'],
                tags.fieldset[group]
              ],

              tags.fieldset[
                tags.legend['Contact Information'],
                tags.fieldset[contacts]
              ],

              tags.fieldset[
                tags.legend['Place'],
                  tags.label['Place Name:',
                             tags.input(_type='text',_class='text',name='place_name',id='place_name',size='80',value=placedict['name'])],
                  tags.br,
                  placetip,
                  tags.label['Place Phone:',
                             tags.input(_type='text',_class='text',name='place_phone',id='place_phone',size='80',value=placedict['phone'])],
                  tags.fieldset[
                    tags.legend['Place Address'],
                    tags.textarea(style='width:100%;',rows=3,cols=80,name='place_address',id='place_address')[placedict['address']]
                    ],
                  tags.label['Place Locale',place_locale]
              ],

              tags.fieldset[
                tags.legend['Accessibility of Event'],
                event_access
              ],
              
              tags.fieldset[
                tags.legend['Event Start'],

                dateerror,
                
                date_once,
                
                date_recurring,
                
                tags.fieldset[
                  tags.input(_type='radio',name='radio_date',id='radio_freeform',value='freeform'),
                  tags.label(_for='radio_freeform')[tags.acronym(title='Use this if the other methods don\'t make sense.')["Freeform"]],
                  tags.div[
                    tags.input(type='text',_class='text',size='80',id='freeform',name='freeform',value='Explain when your event occurs and moderators will schedule it.',
                               onmousedown='document.createevent.radio_date[2].checked=true;')
                    ]
                  ]
                ],

              tags.fieldset[
                tags.legend['Event Description'],
                
                tags.fieldset[
                  tags.legend['Full Description'],
                  tags.textarea(style='width:100%;',rows=20,cols=80,name='text',id='text')[request.session.mind[CreateEventPage].text]],
              
                tags.fieldset[
                  tags.legend['Brief'],
                  tags.textarea(style='width:100%;',rows=4,cols=80,name='brief',id='brief')[request.session.mind[CreateEventPage].brief]],
                ],
              tags.div(id='submitholder')
              ]
            ],

            self.preview(request)
          ]

class EventListPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'event-list.html'))
    watermark = 'event'

                                        # paging support
    perpage = Config.events_per_page
    currentpage = 0
    previous = None
    next = None
    totalpages = 0

    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        rtn = GenericPage.render_links(self,ctx,data)
        request = inevow.IRequest(ctx)

        if self.month is None:
            ## FIXME
            rtn.append(tags.link(rel='up',href=makeRelative(request.uri,'/')))
            return rtn
        
        if self.previous is not None:
            rtn.append(tags.link(rel='previous',href=addGetVariable(request.uri,'begin',self.previous)))
        if self.next is not None:
            rtn.append(tags.link(rel='next',href=addGetVariable(request.uri, 'begin',self.next)))
        rtn.append(tags.link(rel='stylesheet',href=makeRelative(request.uri,'/activist-form.css')))

        rtn.append(tags.link(rel='up',href=makeRelative(request.uri,'/event/time/%04d')%(self.year,)))

        ## if there's no paging, we'll make the next or previous thing
        ## go to the next or previous month

        if self.totalpages == 0:
            t = (self.year,self.month)
            prev = (t[0],t[1]-1)
            if prev[1] < 1: prev = (prev[0]-1,12)
            next = (t[0],t[1]+1)
            if next[1] > 12: next = (next[0]+1,1)
            rtn.append(tags.link(rel='previous',href=makeRelative(request.uri,'/event/time/%d/%02d')%prev,name=calendar.month_name[prev[1]]))
            rtn.append(tags.link(rel='next',href=makeRelative(request.uri,'/event/time/%d/%02d')%next,name=calendar.month_name[next[1]]))
        
        return rtn

    def renderHTTP(self,ctx):
        if inevow.IRequest(ctx).session.mind.object is None:
            return inevow.IRequest(ctx).redirect('/event/time')
        return super(EventListPage,self).renderHTTP(ctx)
    
    def beforeRender(self,ctx):
        GenericPage.beforeRender(self,ctx)
        
        request = inevow.IRequest(ctx)
        #print "list:beforerender",request.args
        mind = request.session.mind
        self.removed = 0

        if inevow.IRequest(ctx).session.mind.object is None:
            return
        
        viewUnmoderated = hasattr(request,'viewunmoderated')
        if not viewUnmoderated and inevow.IRequest(ctx).session.mind.object and len(inevow.IRequest(ctx).session.mind.object) > 1:
            (self.removed,inevow.IRequest(ctx).session.mind.object) = filter.moderatedListings(inevow.IRequest(ctx).session.mind.object,request)

        if request.args.has_key('begin'):
            try:
                self.currentpage = int(request.args['begin'][0])
                del request.args['begin']
            except ValueError:
                self.currentpage = 0

        self.moderator = False
        for listing in inevow.IRequest(ctx).session.mind.object:
            if mind.canModerate(listing):
                self.moderator = True
                break

        self.totalpages = len(inevow.IRequest(ctx).session.mind.object) / self.perpage
        begin = self.currentpage #* self.perpage
        end = begin + self.perpage
        self.previous = begin-self.perpage
        if self.previous < 0: self.previous = 0
        if self.previous == begin: self.previous = None
        self.next = begin+self.perpage
        if self.next >= len(inevow.IRequest(ctx).session.mind.object)-1: self.next = None
       
        n = inevow.IRequest(ctx).session.mind.object[begin:end]
        if len(n) == 0:
            self.begin = 0
            inevow.IRequest(ctx).session.mind.object = inevow.IRequest(ctx).session.mind.object[:self.perpage]
        else:
            inevow.IRequest(ctx).session.mind.object = n
            
    def crumbExtras(self,ctx):
        if self.month is None:
            other = [
                tags.a(href=makeRelative(request.uri,'/event/time/'))['time'],
                ' < ',
                '%d'%self.year,
                ' < ',
                tags.a(href=makeRelative(request.uri,'/event/time') + '/%d'%(self.year,))[str(self.year)]]
        else:
            other = [
                tags.a(href=makeRelative(request.uri,'/event/time/'))['time'],
                ' < ',
                '%d'%self.year,
                ' < ',
                tags.a(href=makeRelative(request.uri,'/event/time') + '/%d/%02d'%(self.year,self.month))['%02d'%self.month]]
        return [ tags.a(href=makeRelative(request.uri,'/event/'))['events'],
                 ' < ',
                 other ]

        if self.option:
            other = ['( ']
            for x in ['time','place','category']:
                if x != self.option:
                    other.append(tags.a(href=makeRelative(request.uri,'/event/%s')%x)[x])
                else:
                    other.append(x)
                other.append(' | ')
            other[-1] = ' )'
            return [ tags.a(href=makeRelative(request.uri,'/event/'))['events'],
                     ' < ',
                     other ]
        else:
            return [ 'events' ]

    def render_paging(self,ctx,data):
        if 0:
            rtn = ctx.tag
            rtn['[']
            if self.previous is not None:
                rtn[tags.a(href=addGetVariable(request.uri,'begin',self.previous))['previous page']]
            if self.next is not None:
                rtn[', ',tags.a(href=addGetVariable(request.uri, 'begin',self.next))['next page']]
            rtn[']']
            
        if self.previous == None and self.next == None:
            return ctx.tag

        request = inevow.IRequest(ctx)
        ctx.tag['pages: ']
        if self.previous is not None:
            ctx.tag[tags.a(href=addGetVariable(request.uri,'begin',self.previous))['previous']]
        ctx.tag[' [']
        for x in range(0,self.totalpages):
            if self.currentpage == x*self.perpage:
                ctx.tag[tags.span(style='font-weight:bold;')[str(x)]]
            else:
                ctx.tag[tags.a(href=addGetVariable(request.uri,'begin',int(x*self.perpage)))[str(x)]]
            if x != (self.totalpages)*self.perpage:
                ctx.tag[' | ']
                            
        ctx.tag['] ']
        if self.next is not None:
            ctx.tag[tags.a(href=addGetVariable(request.uri, 'begin',self.next))['next']]
        return ctx.tag
        return rtn

    def render_monthnavigation(self,ctx,data):
        request = inevow.IRequest(ctx)
        now = time.localtime()
        maxyear = now.tm_year + 1
        minyear = now.tm_year - 5
        if self.month is None:
            this = self.year
            if this-2 >= minyear:
                ctx.tag[
                    tags.a(href=makeRelative(request.uri,'/event/time') + '/%d'%(this-2,))[str(this-2)],
                    ' << ',
                    ]
            if this-1 >= minyear:
                ctx.tag[
                    tags.span(style="font-size:130%;")[tags.a(href=makeRelative(request.uri,'/event/time') + '/%d'%(this-1,))[str(this-1)]],
                    ]
            
            ctx.tag[
                ' < ',
                tags.span(style="font-size:160%;")[tags.b[str(this)]],
                ' > ']
            
            if this+1 <= maxyear:
                ctx.tag[
                    tags.span(style="font-size:130%;")[tags.a(href=makeRelative(request.uri,'/event/time')+'/%d'%(this+1,))[str(this+1)]],
                ' >> ']
            if this+2 <= maxyear:
                ctx.tag[
                    tags.a(href=makeRelative(request.uri,'/event/time')+'/%d'%(this+2,))[str(this+2)]
                    ]
            return ctx.tag
            
        request = inevow.IRequest(ctx)
        t = (self.year,self.month)
        twoprev = (t[0],t[1]-2)
        if twoprev[1] < 1: twoprev = (twoprev[0]-1,12+(t[1]-2))
        prev = (t[0],t[1]-1)
        if prev[1] < 1: prev = (prev[0]-1,12)
        next = (t[0],t[1]+1)
        if next[1] > 12: next = (next[0]+1,1)
        nexttwo = (t[0],next[1]+1)
        if nexttwo[1] > 12: nexttwo = (nexttwo[0]+1,1)
        if twoprev[0] >= minyear:
            ctx.tag[
                tags.a(href=makeRelative(request.uri,'/event/time')+'/%d/%02d'%twoprev)[calendar.month_name[twoprev[1]]],
                ' << ',
                ]
        if prev[0] >= minyear:
            ctx.tag[
                tags.span(style="font-size:130%")[tags.a(href=makeRelative(request.uri,'/event/time')+'/%d/%02d'%prev)[calendar.month_name[prev[1]]]],
                " < "
                ]
            
        ctx.tag[
            tags.span(style="font-size:160%")[tags.b[calendar.month_name[t[1]],", ",tags.a(href=makeRelative(request.uri,'/event/time')+'/%04d'%(t[0],))[t[0]]]],
            " > "]
        
        if next[0] <= maxyear:
            ctx.tag[
                tags.span(style="font-size:130%")[tags.a(href=makeRelative(request.uri,'/event/time') + '/%d/%02d'%next)[calendar.month_name[next[1]]]],
                " >> "
                ]
            if nexttwo[0] <= maxyear:
                ctx.tag[
                    tags.a(href=makeRelative(request.uri,'/event/time')+'/%d/%02d'%nexttwo)[calendar.month_name[nexttwo[1]]]
                    ]
        return ctx.tag

    def render_printable(self,ctx,data):
        if self.month is None:
            return ''
        
        if len(inevow.IRequest(ctx).session.mind.object) == 0:
            return ''
        request = inevow.IRequest(ctx)
        if hasattr(request,'viewunmoderated'):
            return ctx.tag[
                tags.a(_class='pdf',href=makeRelative(request.uri,'/print/event')+'/%s/%d/%02d/unmoderated'%(self.option,self.year,self.month))[tags.img(_class='pdf',style="border:none;",src='/icon-pdf.png',alt='PDF',title='Download a PDF version of these listings...')]]
        return ctx.tag[
            tags.a(_class='pdf',href=makeRelative(request.uri,'/print/event')+'/%s/%d/%02d'%(self.option,self.year,self.month))[tags.img(_class='pdf',style="border:none;",src='/icon-pdf.png',alt='PDF',title='Download a PDF version of these listings...')]]

    def render_calendar(self,ctx,data):
        request = inevow.IRequest(ctx)
        if self.option != 'time':
            return ''
        request = inevow.IRequest(ctx)
        
        if not inevow.ISession(ctx).mind.option('show-calendar'):
            return ctx.tag(_class='calendar',id='calendar')[tags.div(id='closer')['[',tags.a(href=self.addArg(request,'show-calendar','1'))['show calendar'],']']]

        eventdays = []
        eventcount = {}
        events = {}
        notoncalendar = 0
                                        # always use current month/year

        if self.month == None:
            year = self.year
            return ''

        else:
            todayshowing = True
            month = self.month
            year = self.year
            now = time.localtime()
            today = now.tm_mday
            todayshowing = now.tm_year == year and now.tm_mon == self.month

            for e in request.session.mind.object:
                day = e.info().whenRaw().localtime()[2]
                if year != e.info().whenRaw().localtime()[0] or month != e.info().whenRaw().localtime()[1]:
                                            # event not during this month
                    notoncalendar = notoncalendar + 1
                    continue

                if not day in eventdays:
                    eventdays.append(day)
                    eventcount[day] = 1
                    events[day] = [e]
                else:
                    eventcount[day] = eventcount[day] + 1
                    events[day].append(e)
                

        monthname = calendar.month_name[month]
        rowcols = [tags.tr[tags.th['Monday'],tags.th['Tuesday'],tags.th['Wednesday'],tags.th['Thursday'],tags.th['Friday'],tags.th['Saturday'],tags.th['Sunday']]]
        for row in calendar.monthcalendar(year,month):
            thisrow = []
            for cell in row:
                txt = '%02d' % cell
                if cell != 0:
                    if cell in eventdays:
                        if cell == today and year == now.tm_year and month == now.tm_mon:
                            td = tags.td(_class="activetoday")
                        else:
                            td = tags.td(_class="active")
                        td[tags.a(_class='day',href='#day_%d'%cell,title='Go to %s %02d (%d events).'%(monthname,cell,eventcount[cell]))[txt]]

                    else:
                        if cell == today and year == now.tm_year and month == now.tm_mon:
                            td = tags.td(_class="today")[txt]
                        else:
                            td = tags.td[txt]
                        if calendar.weekday(year,month,cell) == calendar.SUNDAY or \
                               calendar.weekday(year,month,cell) == calendar.SATURDAY:
                            td(_class='weekend')
                    if events.has_key(cell):
                        for event in events[cell]:
                                        # 8594 is right-arrow
                            title = [entities.getEntity(8594),event.info().title()]
                            if Config.shortenCalendarTitles and len(title) > 16:
                                title=title[:10]+'...'+title[-4:]
                            if event.isApproved():
                                td[tags.br,tags.a(title=event.info().title(),href=makeURL(request.uri, event))[title]]
                            else:
                                td[tags.br,tags.a(_class='unapproved',title=event.info().title(),href=makeURL(request.uri, event))[title]]
                    thisrow.append( td )
                else:
                    thisrow.append( tags.td(_class='null')[''] )
            rowcols.append(tags.tr[thisrow])

        gototoday = ''
        if not todayshowing:
            gototoday = tags.div(style='text-align:center;')['Go to today (',tags.a(href=makeRelative(request.uri,'/event/time')+'/%04d/%02d'%(now.tm_year,now.tm_mon))[time.strftime('%B %d, %Y',now)],').']
            
        return ctx.tag(_class='calendar',id='calendar')[ tags.h4[monthname + ', ' + str(year)],
                                                             tags.table(_class='calendar')[rowcols],
#                                                             tags.div(style='text-align:center;')['There are ',tags.b[str(notoncalendar) + ' events'],' showing which do not occur this month.'],
                                                             gototoday,
                                                             tags.div(_id='closer')['[',tags.a(href=self.addArg(request,'show-calendar','0'))['hide calendar'],']']]
            

    def render_datestart(self,ctx,data):
        ctx.tag.clear()
        if not len(inevow.IRequest(ctx).session.mind.object):
            return ctx.tag
        min = inevow.IRequest(ctx).session.mind.object[0].info().when()
        for x in inevow.IRequest(ctx).session.mind.object[1:]:
            y = x.info().when()
            if y < min:
                min = y
        return ctx.tag[min]

    def render_dateend(self,ctx,data):
        ctx.tag.clear()
        if not len(inevow.IRequest(ctx).session.mind.object):
            return ctx.tag
        max = inevow.IRequest(ctx).session.mind.object[0].info().when()
        for x in inevow.IRequest(ctx).session.mind.object[1:]:
            y = x.info().when()
            if y > max:
                max = y
        return ctx.tag[max]

    def render_moderated_number(self,ctx,data):
        return ctx.tag[str(self.removed)]

    def render_viewunmoderated(self,ctx,data):
        request = inevow.IRequest(ctx)
        return ctx.tag(href=makeRelative(request.uri,'/event')+'/%s/%d/%02d/unmoderated'%(self.option,self.year,self.month))

    def render_viewmoderated(self,ctx,data):
        request = inevow.IRequest(ctx)
        return ctx.tag(href=makeRelative(request.uri,'/event')+'/%s/%d/%02d'%(self.option,self.year,self.month))

    def render_moderator(self,ctx,data):
        if self.moderator:
            return ctx.tag
        return ''

    def render_moderation(self,ctx,data):
        if hasattr(ctx.locate(inevow.IRequest),'viewunmoderated'):
            unmoderated = ctx.patternGenerator('unmoderated')
            ctx.tag[unmoderated(render=lambda ctx,data:ctx.tag)]

        else:
            if self.removed == 0:
                return ''
            moderated = ctx.patternGenerator('moderated')
            ctx.tag[moderated(render=lambda ctx,data: ctx.tag)]
        return ctx.tag

    def render_listings(self,ctx,data):
        current = None
        place = None
        category = None

        body = []
        showing = ctx.patternGenerator('showing')

        totalevents = 0
        if self.month is None:
            for month in range(1,13):
                events = DataStore.getEventsForMonth(self.year, month)
                if len(events) > 0:
                    body.append(tags.h2[calendar.month_name[month]])
                    lst = []
                    for event in events:
                        lst.append(tags.li[tags.span(render=lambda ctx,data:view.IMicroView(data),data=event)])
                        totalevents = totalevents + 1
                    body.append(tags.ul[lst])

            if totalevents == 0:
                ctx.tag[tags.p(style='font-size:130%;')['No events during %04d.'%self.year]]
            else:
                body.append(tags.p['There are %d events listed during %04d.'%(totalevents,self.year)])
                
            return ctx.tag[body]

        body = [showing()]
        
        if not inevow.IRequest(ctx).session.mind.object or len(inevow.IRequest(ctx).session.mind.object) == 0:
            return tags.p(style='text-align: center; font-size:200%;')['No events during ',calendar.month_name[self.month],', ',self.year]

                                        # inevow.IRequest(ctx).session.mind.object has been filtered
                                        # in self.setup

        if self.option == 'category':
            for category in DataStore.getCategories():
                body.append( tags.h2(_class="date")[category.title()] )
                for listing in inevow.IRequest(ctx).session.mind.object:
                    if category in listing.categories():
                        body.append(tags.div(render=lambda ctx,data:view.ISummaryView(data),data=listing))

        else:
            for listing in inevow.IRequest(ctx).session.mind.object:
                if not current and self.option ==  'time':
                    current = listing.info().whenRaw().localtime()
                    body.append( tags.a(name="day_%s"%time.strftime('%d',current))[' '] )
                    body.append( tags.h2(_class="date")[time.strftime('%A, %B %d %Y',current)] )
                elif not place and self.option == 'place':
                    place = listing.info().where()
                    body.append( tags.h2(_class="date")[place._name] )

                if current:
                    date = listing.info().whenRaw().localtime()
                    if date.tm_year != current.tm_year or \
                           date.tm_yday != current.tm_yday:
                        current = date
                        body.append( tags.a(name="day_%s"%time.strftime('%d',current))[' '] )
                        body.append( tags.h2(_class="date")[time.strftime('%A, %B %d %Y',current)] )
                if place:
                    if listing.info().where()._name.lower() != place._name.lower():
                        place = listing.info().where()
                        body.append( tags.h2(_class="date")[place._name] )

                body.append(tags.div(render=lambda ctx,data:view.ISummaryView(data),data=listing))

        ctx.tag[body]
        return ctx.tag
    

class EventPage(GenericPage):
    docFactory = loaders.htmlfile(os.path.join(Config.templatePath,'event-fullview-page.html'))
    watermark = 'event'

    def crumbExtras(self,ctx):
        title = inevow.IRequest(ctx).session.mind.object.info().title()
        if len(title) > 30:
            title = title[:20]+'...'+title[-10:]
        other = ['( ']
        for x in ['time','place','category']:
            other.append(tags.a(href=makeRelative(request.uri,'/event')+'/%s'%x)[x])
            other.append(' | ')
        other[-1] = ' )'
        return [ tags.a(href=makeRelative(request.uri,'/event/'))['events'],
                 ' < ',
                 other,
                 ' < ',
                 title ]
        
    def renderHTTP(self,ctx):
        if inevow.IRequest(ctx).session.mind.object is None:
            return inevow.IRequest(ctx).redirect('/event/time')
        return super(EventPage,self).renderHTTP(ctx)

    def render_bodylinks(self,ctx,data):
        request = inevow.IRequest(ctx)
        d = request.session.mind.object
        rtn = ctx.tag
        rtn['[ This event is most-recently listed in ']
        rtn[tags.a(href=makeTimeURLFromEvent(request.uri, d))[d.info().whenRaw().format('%B %Y')]]
        rtn[' ]']
        return rtn
    
    def render_links(self,ctx,data):
        request = inevow.IRequest(ctx)
        obj = request.session.mind.object
        rtn = GenericPage.render_links(self,ctx,data)
        rtn.append( tags.link(rel='up',href=makeTimeURLFromEvent(request.uri, obj),title=obj.info().whenRaw().format('%B %Y')) )
        prev = obj.findNextChronological()
        next = obj.findPreviousChronological()
        if next:
            rtn.append( tags.link(rel='next',href=makeRelative(request.uri,'/event')+'/%s'%next.id(),title='Earlier Chronological Event ("%s")'%next.info().title()) )
        if prev:
            rtn.append( tags.link(rel='prev',href=makeRelative(request.uri,'/event')+'/%s'%prev.id(),title='Later Chronological Event ("%s")'%prev.info().title()) )
        return rtn

    def data_info(self,ctx,data):
        return inevow.IRequest(ctx).session.mind.object.info()

    def render_title(self,ctx,data):
        ctx.tag[ inevow.IRequest(ctx).session.mind.object.info().title() ]
        return ctx.tag

    def render_body(self,ctx,data):
        data = inevow.IRequest(ctx).session.mind.object
        return tags.div(render=lambda ctx,data:view.IFullView(data),data=data)
        



class LoginPage(GenericPage):
    docFactory = loaders.htmlfile(Config.template('login.html'))
    watermark = 'participate'

    def render_form(self,ctx,data):
        request = ctx.locate(inevow.IRequest)
        redir = ctx.arg('redirect')
        if not redir: redir = ''
        
        if request.session.mind.isAnonymous():
            failure = ''
            if request.args.has_key('login-failure'):
                failure = tags.div(_class='warning')[request.args['login-failure'][0]]
            message = ''
            if request.args.has_key('required'):
                message = tags.div(_class='message')['You are required to log in to access ',tags.code[redir]]
            ctx.tag[
                tags.form(action=guard.LOGIN_AVATAR+redir,method="post")[
                  failure,
                  message,
                  tags.fieldset[
                    tags.div[
                      tags.label(_for="username")[ "Username:" ],
                      tags.input(id='username',type='text',_class='text',name='username')
                    ],
                    tags.div[
                      tags.label(_for='password')[ "Password:" ],
                      tags.input(id='password',_class='text',type='password',name='password')
                    ]
                  ],
                  tags.input(type='submit',_class='button',value="Log In")
                ]
            ]

        else:
            ## FIXME; put in template with pattern
            ctx.tag[
                tags.div(style="border: 2px solid red; padding: 10px; font: 140% sans-serif; text-align:center;")[
                  'You are already logged-in as: ',
                  tags.span(style="font-weight:bold;")[request.session.mind.username()],
                  '. You may ',
                  tags.a(href=makeRelative(request.uri,'/logout'))['logout'],
                  ' if you wish.']
              ]

        return ctx.tag
            

__all__ = ["StaticPage",
           "GenericPage",
           "Error404",
           "YearTooBig",
           "AdminPage",
           "AdminMessages",
           "CreateArticlePage",
           "ArticlePage",
           "WaitPage",
           "PersonPage",
           "CommentPage",
           "CreateCommentPage",
           "CreateUserPage",
           "CreateMediaPage",
           "CreateGroupPage",
           "PersonListPage",
           "GroupListPage",
           "GroupPage",
           "CreateEventPage",
           "EventListPage",
           "EventPage",
           "LoginPage",
           "PlacePage"
           ]
           
